Reseller orders
The reseller's orders — list, status counters, detail with live tracking, cancellation, assisted-order confirmation or rejection, reorder, documents — and V1 return requests on delivered orders.
These operations power the « Commandes » tab of the reseller app (Imad, MRA-05, returns in MRA-10) and the orders pages of the reseller web (Anis, WEB-04); they are built in SRV-16 and SRV-20.
- Only my orders. Every operation is scoped to the caller's reseller (cahier §5.4 « Le revendeur ne voit que ses commandes »); another reseller's order is
404 NOT_FOUND.DRAFTorders (assisted orders still being prepared by the delegate) are never visible. - The status is the back-office's status (ORD-07): the apps show the exact
OrderStatuslabel, a five-step tracker and the dated history. Every order waits for an explicit back-office confirmation (ORD-14);awaitingConfirmationsays who is expected to act. - Live. While the app or page is open,
events.streampushesorder.status_changed,order.modified,assisted_order.awaiting_confirmationandreturn.status_changed; every change is also a push and an inbox notification (ORD-15). - The server decides what is allowed.
allowedActionslists the buttons to show; the apps never re-implement the rules of the order lifecycle. - Returns are V1 (ORD-13): a return is requested on a delivered order within
returns.windowDays(default 7). After-sales claims under warranty are support tickets of typeAFTER_SALES(support).
| Method | Path | Operation | Access | Apps | Release |
|---|---|---|---|---|---|
| GET | /orders | orders.listMy orders with status filter and search (cursor pagination) | self | RA · RW | V1 |
| GET | /orders/summary | orders.summaryCounters by status for the tracking header | self | RA · RW | V1 |
| GET | /orders/{orderId} | orders.getOrder detail: lines, totals, status timeline, delivery, documents, allowed actions | self | RA · RW | V1 |
| POST | /orders/{orderId}/cancel | orders.cancelCancel my order while the rules allow it | selfIdempotency-Key | RA · RW | V1 |
| POST | /orders/{orderId}/confirm | orders.confirmAssistedConfirm an assisted order prepared by my delegate | selfIdempotency-Key | RA · RW | V1 |
| POST | /orders/{orderId}/reject | orders.rejectAssistedReject an assisted order with a reason | selfIdempotency-Key | RA · RW | V1 |
| POST | /orders/{orderId}/reorder | orders.reorderCopy the order's available lines into my cart | selfIdempotency-Key | RA · RW | V1 |
| GET | /orders/{orderId}/documents/{type} | orders.documentSigned URL of the invoice or delivery note PDF | self | RA · RW | V1 |
| POST | /orders/{orderId}/returns | returns.createRequest a return of delivered items (lines, quantities, reason, photos) | selfIdempotency-Key | RA · RW | V1 |
| GET | /returns | returns.listMy return requests with status | self | RA · RW | V1 |
| GET | /returns/{returnId} | returns.getReturn detail with lines, status history and credit note | self | RA · RW | V1 |
Every operation can also answer 401 UNAUTHENTICATED, 403 ACCOUNT_SUSPENDED, 403 PERMISSION_DENIED (caller is not a reseller), 426 UPGRADE_REQUIRED, 429 RATE_LIMITED, 500 INTERNAL_ERROR and 503 MAINTENANCE; operations with Idempotency-Key required also 400 IDEMPOTENCY_KEY_REQUIRED, 409 IDEMPOTENCY_KEY_REUSED and 409 IDEMPOTENCY_IN_PROGRESS. See the error registry.

Tracker and allowed actions
progressStep | Tracker label | Order statuses |
|---|---|---|
| 0 | En attente | SUBMITTED, PENDING |
| 1 | Validée | VALIDATED |
| 2 | En préparation | PREPARING, READY |
| 3 | Expédiée | SHIPPED |
| 4 | Livrée | DELIVERED |
null | off the tracker, with the reason | REFUSED, CANCELLED, RETURNED |
allowedActions value | Present when | Calls |
|---|---|---|
CANCEL | Status in orders.cancelAllowedStatuses (default SUBMITTED, PENDING) and not an assisted order awaiting my confirmation | orders.cancel |
CONFIRM, REJECT | Assisted order PENDING with confirmation AWAITING_RESELLER, before assisted.expiresAt | orders.confirmAssisted, orders.rejectAssisted |
REORDER | DELIVERED, CANCELLED, REFUSED, RETURNED | orders.reorder |
REQUEST_RETURN | DELIVERED, now ≤ returnableUntil, at least one line with returnableQty above 0 | returns.create |
OPEN_CLAIM | DELIVERED, now ≤ claimableUntil, something left to claim | tickets.create with type: AFTER_SALES |
DOWNLOAD_INVOICE, DOWNLOAD_DELIVERY_NOTE | The document exists (documents[].available) | orders.document |
CONTACT_SUPPORT | Always | tickets.create with orderId (cahier §5.4 « Contacter le support ») |
Schemas
PromotionApplied, LineTotals, AppliedPromotion and DisplayTotals are defined on the cart page; Totals, Address, StatusChange, FileRef and Price in the shared schemas.
type OrderAction = "CANCEL" | "CONFIRM" | "REJECT" | "REORDER" | "REQUEST_RETURN" | "OPEN_CLAIM"
| "DOWNLOAD_INVOICE" | "DOWNLOAD_DELIVERY_NOTE" | "CONTACT_SUPPORT";
type PendingReason = "AWAITING_RESELLER" | "CREDIT_OVER_LIMIT" | "RESELLER_NOT_VALIDATED" | "ON_HOLD";
interface OrderSummary {
id: Uuid;
number: string; // CMD-2026-000158
status: OrderStatus;
progressStep: number | null; // 0–4, null off the tracker
createdAt: Iso; // placed (checkout) or sent for my confirmation (assisted)
itemsCount: number; // lines — « 3 articles »
unitsCount: number;
firstItem: { name: string; imageUrl: string | null };
totals: { totalTtc: Money; currency: "DZD" };
paymentMethod: PaymentMethod;
isAssisted: boolean;
awaitingMyConfirmation: boolean; // pinned « À confirmer »
assistedExpiresAt: Iso | null;
hasInvoice: boolean;
}
interface OrderDetail {
id: Uuid;
number: string;
version: number; // +1 on every change; compare with live events
status: OrderStatus;
progressStep: number | null;
awaitingConfirmation: "BACK_OFFICE" | "RESELLER" | null; // who must act before preparation
pendingReasons: PendingReason[]; // non-empty only when PENDING
channel: OrderChannel;
isFirstOrder: boolean;
createdAt: Iso;
submittedAt: Iso | null;
validatedAt: Iso | null; // back-office confirmation
updatedAt: Iso;
currency: "DZD";
itemsCount: number;
unitsCount: number;
lines: OrderLine[];
totals: Totals;
displayTotals: DisplayTotals;
appliedPromotions: AppliedPromotion[];
address: Address; // snapshot taken at checkout
note: string | null;
paymentMethod: PaymentMethod;
paymentStatus: {
invoiced: boolean;
invoiceId: Uuid | null;
invoiceNumber: string | null; // FAC-2026-000124
invoiceStatus: InvoiceStatus | null;
dueDate: string | null; // YYYY-MM-DD
amountPaid: Money; // confirmed payments allocated to this order or its invoice
creditNotesTotal: Money;
balance: Money;
pendingVerificationAmount: Money; // declared, not yet verified
};
delivery: {
status: DeliveryStatus | null; // null before the back-office confirmation
mode: DeliveryMode | null;
carrierName: string | null;
trackingNumber: string | null;
trackingUrl: string | null;
driverName: string | null; // OWN_FLEET
driverPhone: string | null;
deliveryNoteNumber: string | null; // BL-2026-000141, from READY
shippedAt: Iso | null;
deliveredAt: Iso | null;
recipientName: string | null;
};
statusHistory: StatusChange[]; // oldest first, public reasons only
changes: { at: Iso; reason: string; lines: { sku: string; name: string; fromQty: number; toQty: number }[] }[];
documents: { type: "INVOICE" | "DELIVERY_NOTE"; number: string | null; available: boolean; generatedAt: Iso | null }[];
assisted: {
isAssisted: boolean;
delegate: { id: Uuid; displayName: string; phone: string } | null;
confirmation: AssistedConfirmation | null;
expiresAt: Iso | null;
confirmedAt: Iso | null;
rejectionReason: string | null;
};
cancellation: { reasonCode: string; reasonLabel: string; comment: string | null; at: Iso; by: "RESELLER" | "HOCO" | "SYSTEM" } | null;
refusal: { reasonCode: string; reasonLabel: string; comment: string | null; at: Iso } | null;
returns: { id: Uuid; number: string; status: ReturnStatus }[];
claims: { id: Uuid; number: string; status: TicketStatus }[]; // AFTER_SALES tickets on this order
returnableUntil: Iso | null; // deliveredAt + returns.windowDays
claimableUntil: Iso | null; // deliveredAt + after-sales window (BR-81)
allowedActions: OrderAction[];
}
interface OrderLine {
id: Uuid; // OrderItem id — used as orderItemId by returns and claims
product: { id: Uuid; name: string; brandName: string; imageUrl: string | null }; // snapshot
variant: { id: Uuid; sku: string; label: string };
qty: number;
price: Price; // snapshot unit price (customer type) before promotion
promotionApplied: PromotionApplied | null;
lineTotals: LineTotals;
originalQty: number | null; // set when the back-office changed the quantity (ORD-06)
returnedQty: number; // in returns not REJECTED
claimedQty: number; // in after-sales claims not REJECTED
returnableQty: number; // delivered − returned − claimed, 0 before delivery
}
interface ReturnView {
id: Uuid;
number: string; // RET-2026-000012
status: ReturnStatus; // REQUESTED, APPROVED, IN_TRANSIT, RECEIVED, REFUNDED, REJECTED
origin: "RESELLER" | "AFTER_SALES";
order: { id: Uuid; number: string; deliveredAt: Iso };
ticket: { id: Uuid; number: string } | null; // when created by an after-sales resolution
reason: string;
items: {
id: Uuid;
orderItemId: Uuid;
product: { id: Uuid; name: string; imageUrl: string | null };
variant: { id: Uuid; sku: string; label: string };
qty: number; // requested
unitPrice: Price; // the order line's net unit price (after promotion)
amountTtc: Money;
photos: FileRef[]; // TICKET_ATTACHMENT, signed URLs
receivedQty: number | null; // after reception
acceptedQty: number | null; // credited
refusalReason: string | null; // for units not accepted at inspection
}[];
amountTtc: Money; // value of the requested items
instructions: string | null; // how to send the goods back, set at approval
transport: { mode: "RESELLER_DROP_OFF" | "CARRIER" | "DELEGATE_PICKUP"; scheduledFor: string | null; trackingNumber: string | null } | null;
rejectionReason: string | null;
creditNote: { id: Uuid; number: string; amountTtc: Money; issuedAt: Iso; file: FileRef | null } | null;
statusHistory: StatusChange[];
requestedAt: Iso;
updatedAt: Iso;
}Actors in histories. For the reseller, statusHistory[].actor is the reseller's own user, the assisted-order delegate, { displayName: "Équipe HOCO" } for any back-office user (staff names are not shown to resellers), or null for the system (expiry, automatic moves). reason is the public text of the reason (ReferenceItem label plus the comment written for the reseller), never an internal note.
Operations
GET /orders
orders.list · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-07
Contract 1.1.0 (SRV-41): every OrderSummary carries invoiceStatus (UNPAID, PARTIALLY_PAID, PAID, OVERDUE, CANCELLED; null before invoicing) next to hasInvoice.
Purpose. « Historique des commandes »: my orders, newest first, with the tracker step of each, filterable by status and searchable.
Request
| Query | Type | Required | Default | Notes |
|---|---|---|---|---|
status | OrderStatus, repeatable | no | all but DRAFT | A counter tap sends its statuses: status=SUBMITTED&status=PENDING |
q | string 2–60 | no | — | Order number (158 or CMD-2026-000158), product name or SKU |
from, to | YYYY-MM-DD | no | — | On createdAt, Africa/Algiers days |
awaitingMyConfirmation | true | no | — | Only assisted orders waiting for me |
cursor, limit | — | no | limit 20 | Max 100 |
Response — 200 OK: { items: OrderSummary[], nextCursor }.
{
"items": [
{
"id": "01a1a0de-84e0-79ad-9ae7-a8167cc6fcaa",
"number": "CMD-2026-000159",
"status": "PENDING",
"progressStep": 0,
"createdAt": "2026-11-03T10:05:00Z",
"itemsCount": 2,
"unitsCount": 15,
"firstItem": { "name": "HOCO N32 Chargeur rapide 30W (USB-C + USB-A)", "imageUrl": "https://cdn.hoco.dz/products/n32/n32-black-1-240.webp" },
"totals": { "totalTtc": 2495000, "currency": "DZD" },
"paymentMethod": "CASH_ON_DELIVERY",
"isAssisted": true,
"awaitingMyConfirmation": true,
"assistedExpiresAt": "2026-11-06T10:05:00Z",
"hasInvoice": false
},
{
"id": "01a1a0c0-4e80-7095-bb23-dcbea2c9e1e5",
"number": "CMD-2026-000158",
"status": "SUBMITTED",
"progressStep": 0,
"createdAt": "2026-11-03T09:32:00Z",
"itemsCount": 4,
"unitsCount": 41,
"firstItem": { "name": "BOROFONE BW06 Écouteurs TWS", "imageUrl": "https://cdn.hoco.dz/products/bw06/bw06-white-1-240.webp" },
"totals": { "totalTtc": 8038000, "currency": "DZD" },
"paymentMethod": "CASH_ON_DELIVERY",
"isAssisted": false,
"awaitingMyConfirmation": false,
"assistedExpiresAt": null,
"hasInvoice": false
},
{
"id": "01a19cef-7060-7886-83ab-24329f8f21b0",
"number": "CMD-2026-000157",
"status": "VALIDATED",
"progressStep": 1,
"createdAt": "2026-11-02T15:45:00Z",
"itemsCount": 4,
"unitsCount": 52,
"firstItem": { "name": "ACEFAST A45 Chargeur rapide", "imageUrl": "https://cdn.hoco.dz/products/a45/a45-black-1-240.webp" },
"totals": { "totalTtc": 18500000, "currency": "DZD" },
"paymentMethod": "CASH_ON_DELIVERY",
"isAssisted": false,
"awaitingMyConfirmation": false,
"assistedExpiresAt": null,
"hasInvoice": false
},
{
"id": "01a1910c-39e0-71c4-81d8-f9f786e70645",
"number": "CMD-2026-000156",
"status": "PREPARING",
"progressStep": 2,
"createdAt": "2026-10-31T08:21:00Z",
"itemsCount": 2,
"unitsCount": 8,
"firstItem": { "name": "HOCO J86A Power Bank 10000mAh", "imageUrl": "https://cdn.hoco.dz/products/j86a/j86a-black-1-240.webp" },
"totals": { "totalTtc": 2460000, "currency": "DZD" },
"paymentMethod": "CREDIT",
"isAssisted": false,
"awaitingMyConfirmation": false,
"assistedExpiresAt": null,
"hasInvoice": false
},
{
"id": "01a187cf-6b40-7c6f-894d-b1f256947da9",
"number": "CMD-2026-000155",
"status": "SHIPPED",
"progressStep": 3,
"createdAt": "2026-10-29T13:18:00Z",
"itemsCount": 2,
"unitsCount": 23,
"firstItem": { "name": "HOCO X89 Câble USB-C", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"totals": { "totalTtc": 3685000, "currency": "DZD" },
"paymentMethod": "CASH_ON_DELIVERY",
"isAssisted": false,
"awaitingMyConfirmation": false,
"assistedExpiresAt": null,
"hasInvoice": true
}
],
"nextCursor": "eyJjIjoiMjAyNi0xMC0yOVQxMzoxODowMFoiLCJpIjoiMDFhMTg3Y2YifQ"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Unknown status, DRAFT requested, q under 2 characters, from after to. |
Rules & side effects — read-only, sorted by createdAt descending. The list updates live: on order.status_changed the client patches the item's status and progressStep from the event and refreshes the counters.
Used by — RA-21 (« Commandes »), RW-08, the « Commandes » quick tile of RA-09.
GET /orders/summary
orders.summary · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-07
Purpose. The five counters of « Statut des commandes » (reference screen 6) and the assisted order waiting for my confirmation, in one small call.
Request
| Query | Type | Required | Notes |
|---|---|---|---|
from | YYYY-MM-DD | no | Count only orders created since that day (default: all) |
Response — 200 OK.
{
"tracker": [
{ "step": "AWAITING", "progressStep": 0, "statuses": ["SUBMITTED", "PENDING"], "count": 2 },
{ "step": "VALIDATED", "progressStep": 1, "statuses": ["VALIDATED"], "count": 4 },
{ "step": "PREPARING", "progressStep": 2, "statuses": ["PREPARING", "READY"], "count": 3 },
{ "step": "SHIPPED", "progressStep": 3, "statuses": ["SHIPPED"], "count": 2 },
{ "step": "DELIVERED", "progressStep": 4, "statuses": ["DELIVERED"], "count": 6 }
],
"byStatus": {
"SUBMITTED": 1, "PENDING": 1, "VALIDATED": 4, "PREPARING": 2, "READY": 1,
"SHIPPED": 2, "DELIVERED": 6, "REFUSED": 0, "CANCELLED": 1, "RETURNED": 0
},
"total": 18,
"toConfirm": {
"orderId": "01a1a0de-84e0-79ad-9ae7-a8167cc6fcaa",
"number": "CMD-2026-000159",
"totalTtc": 2495000,
"delegateName": "Yacine Belkacem",
"expiresAt": "2026-11-06T10:05:00Z"
},
"openReturns": 0
}| Field | Notes |
|---|---|
tracker[].step | AWAITING, VALIDATED, PREPARING, SHIPPED, DELIVERED — the client shows En attente, Validées, En préparation, Expédiées, Livrées; a tap lists statuses. |
toConfirm | The assisted order awaiting my confirmation (at most one per reseller — lifecycle rule), pinned « À confirmer »; null when none. |
openReturns | Return requests not yet REFUNDED or REJECTED. |
Errors — none specific.
Rules & side effects — read-only; counts by status of the reseller's non-draft orders. The app recomputes the counters locally from order.status_changed events and re-reads this call on resume.
Used by — header of RA-21, RW-08; the ORDERS badge of the home quick tiles.
GET /orders/{orderId}
orders.get · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-06, ORD-07, ORD-15
Purpose. Order detail and tracking: lines, totals, address, payment and invoice state, delivery and tracking number, dated status history, documents, assisted-order block, returns and claims, and the actions allowed now.
Request — path orderId. If-None-Match with the last ETag ("v{version}") returns 304 when nothing changed.
Response — 200 OK, ETag: "v6": an OrderDetail. Example: an order shipped by carrier, invoiced, not yet delivered.
{
"id": "01a187cf-6b40-7c6f-894d-b1f256947da9",
"number": "CMD-2026-000155",
"version": 6,
"status": "SHIPPED",
"progressStep": 3,
"awaitingConfirmation": null,
"pendingReasons": [],
"channel": "RESELLER_APP",
"isFirstOrder": false,
"createdAt": "2026-10-29T13:18:00Z",
"submittedAt": "2026-10-29T13:18:00Z",
"validatedAt": "2026-10-29T14:02:00Z",
"updatedAt": "2026-10-30T15:05:10Z",
"currency": "DZD",
"itemsCount": 2,
"unitsCount": 23,
"lines": [
{
"id": "01a187cf-6b40-7ed2-a90c-470faa12c942",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 20,
"price": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 2100840, "discountHt": 0, "totalHt": 2100840, "vat": 399160, "totalTtc": 2500000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
},
{
"id": "01a187cf-6b40-70a2-8f15-fa52ec7fed43",
"product": { "id": "01a08fe8-e500-7017-b062-03c53547a2be", "name": "ACEFAST A45 Chargeur rapide", "brandName": "ACEFAST", "imageUrl": "https://cdn.hoco.dz/products/a45/a45-black-1-240.webp" },
"variant": { "id": "01a08fe8-e8e8-762d-81d1-ab0daa011e53", "sku": "A45_BLACK_EU", "label": "Noir" },
"qty": 3,
"price": { "ht": 331933, "ttc": 395000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 995799, "discountHt": 0, "totalHt": 995799, "vat": 189201, "totalTtc": 1185000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
}
],
"totals": { "subtotalHt": 3096639, "discountTotal": 0, "deliveryFee": 0, "vatTotal": 588361, "totalTtc": 3685000, "currency": "DZD" },
"displayTotals": { "itemsTtc": 3685000, "discountTtc": 0, "deliveryFeeTtc": 0, "totalTtc": 3685000 },
"appliedPromotions": [],
"address": {
"id": "01a12f63-efa8-7f36-87fc-e386a4e58a22",
"label": "Magasin",
"recipientName": "Mohamed Benali",
"phone": "+213555123456",
"wilayaCode": "31",
"wilayaName": "Oran",
"communeId": "01a0c3e1-5d20-7a10-8b31-310300000003",
"communeName": "Bir El Djir",
"addressLine": "Cité USTO, local n° 14",
"landmark": "En face de la pharmacie Benyahia",
"isDefault": true
},
"note": null,
"paymentMethod": "CASH_ON_DELIVERY",
"paymentStatus": {
"invoiced": true,
"invoiceId": "01a18d53-2980-79c5-a4cd-4c9ba7aef490",
"invoiceNumber": "FAC-2026-000124",
"invoiceStatus": "UNPAID",
"dueDate": "2026-11-29",
"amountPaid": 0,
"creditNotesTotal": 0,
"balance": 3685000,
"pendingVerificationAmount": 0
},
"delivery": {
"status": "SHIPPED",
"mode": "CARRIER",
"carrierName": "Rapid Express DZ",
"trackingNumber": "RX-31-884215",
"trackingUrl": "https://suivi.rapidexpress.dz/RX-31-884215",
"driverName": null,
"driverPhone": null,
"deliveryNoteNumber": "BL-2026-000141",
"shippedAt": "2026-10-30T15:05:00Z",
"deliveredAt": null,
"recipientName": null
},
"statusHistory": [
{ "from": null, "to": "SUBMITTED", "at": "2026-10-29T13:18:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" } },
{ "from": "SUBMITTED", "to": "VALIDATED", "at": "2026-10-29T14:02:00Z", "actor": { "id": "01a05bfb-7000-7d71-b777-8df1872390e6", "displayName": "Équipe HOCO" } },
{ "from": "VALIDATED", "to": "PREPARING", "at": "2026-10-30T08:10:00Z", "actor": { "id": "01a05bfb-77d0-76d5-a064-865b3b84647b", "displayName": "Équipe HOCO" } },
{ "from": "PREPARING", "to": "READY", "at": "2026-10-30T11:40:00Z", "actor": { "id": "01a05bfb-77d0-76d5-a064-865b3b84647b", "displayName": "Équipe HOCO" } },
{ "from": "READY", "to": "SHIPPED", "at": "2026-10-30T15:05:00Z", "actor": { "id": "01a05bfb-77d0-76d5-a064-865b3b84647b", "displayName": "Équipe HOCO" } }
],
"changes": [],
"documents": [
{ "type": "INVOICE", "number": "FAC-2026-000124", "available": true, "generatedAt": "2026-10-30T15:05:10Z" },
{ "type": "DELIVERY_NOTE", "number": "BL-2026-000141", "available": true, "generatedAt": "2026-10-30T11:40:05Z" }
],
"assisted": { "isAssisted": false, "delegate": null, "confirmation": null, "expiresAt": null, "confirmedAt": null, "rejectionReason": null },
"cancellation": null,
"refusal": null,
"returns": [],
"claims": [],
"returnableUntil": null,
"claimableUntil": null,
"allowedActions": ["DOWNLOAD_INVOICE", "DOWNLOAD_DELIVERY_NOTE", "CONTACT_SUPPORT"]
}| Field | Notes |
|---|---|
awaitingConfirmation | BACK_OFFICE while SUBMITTED or PENDING waiting for HOCO (« En attente de confirmation par HOCO »); RESELLER for an assisted order waiting for me (« À confirmer »); null afterwards. |
pendingReasons | Why a PENDING order is on hold: AWAITING_RESELLER, CREDIT_OVER_LIMIT, RESELLER_NOT_VALIDATED, ON_HOLD (put on hold by HOCO; the public reason is in the last statusHistory entry). |
paymentStatus | Before invoicing (invoiced: false) balance is the order total minus confirmed prepayments. The invoice is issued at billing.invoiceOn (default SHIPPED). |
delivery | trackingUrl comes from the carrier's template; for OWN_FLEET the driver's name and phone replace the carrier (cahier §4.4 « suivi de livraison »). |
changes | Controlled modifications by HOCO (ORD-06): each with the reason shown to the reseller and the old and new quantities; lines[].originalQty marks modified lines. |
returnableQty | Drives the return form: delivered quantity minus what is already in a return or a claim (not rejected). |
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Unknown order, another reseller's order, or a DRAFT. |
Rules & side effects
- Read-only. The reseller sees prices and totals as snapshotted at checkout (or as re-priced at an assisted confirmation, or after a controlled modification) — never today's prices.
- Live updates. The detail screen relies on
events.stream: onorder.status_changedororder.modifiedfor thisorderId, the client refetchesorders.get(withIf-None-Match); onreturn.status_changedit refreshes the returns block. When the stream is not connected (background, network loss) the push notification opens the screen, which then refetches. Nothing is polled. - Internal data never appears: purchase prices, control results, override reasons, internal notes and staff names are back-office only (
admin.orders.get).
Used by — RA-22 (« Suivi de commande »), RA-23, RW-09, deep links of order notifications.
POST /orders/{orderId}/cancel
orders.cancel · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements ORD-10
Purpose. « Annuler la commande » while the back-office has not confirmed it yet.
Request
interface CancelOrderRequest {
reasonCode: string; // ORDER_CANCEL_REASON item open to resellers (reference.lists)
comment?: string; // ≤ 300 characters; required when reasonCode is OTHER
}{ "reasonCode": "DUPLICATE_ORDER", "comment": "Commande passée deux fois, je garde CMD-2026-000157." }Response — 200 OK: the OrderDetail after cancellation.
{
"id": "01a1a0c0-4e80-7095-bb23-dcbea2c9e1e5",
"number": "CMD-2026-000158",
"version": 2,
"status": "CANCELLED",
"progressStep": null,
"awaitingConfirmation": null,
"pendingReasons": [],
"channel": "RESELLER_APP",
"isFirstOrder": false,
"createdAt": "2026-11-03T09:32:00Z",
"submittedAt": "2026-11-03T09:32:00Z",
"validatedAt": null,
"updatedAt": "2026-11-03T09:41:00Z",
"currency": "DZD",
"itemsCount": 4,
"unitsCount": 41,
"lines": [
{
"id": "01a1a0c0-4e80-71dd-aedc-7a6b01b8a49d",
"product": { "id": "01a0901f-d380-7a88-9746-4ec8936c3d68", "name": "BOROFONE BW06 Écouteurs TWS", "brandName": "BOROFONE", "imageUrl": "https://cdn.hoco.dz/products/bw06/bw06-white-1-240.webp" },
"variant": { "id": "01a0901f-d768-77a1-94f2-1bca2251bdcd", "sku": "BW06_WHITE", "label": "Blanc" },
"qty": 6,
"price": { "ht": 250420, "ttc": 298000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 1502520, "discountHt": 0, "totalHt": 1502520, "vat": 285480, "totalTtc": 1788000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
},
{
"id": "01a1a0c0-4e80-7c52-8789-bdc2d7ba3b57",
"product": { "id": "01a08ac2-8900-7f6d-937c-c81b37e89a5b", "name": "HOCO N32 Chargeur rapide 30W (USB-C + USB-A)", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/n32/n32-black-1-240.webp" },
"variant": { "id": "01a08ac2-8ce8-7069-8328-28fd7158a572", "sku": "N32_BLACK_EU", "label": "Noir" },
"qty": 10,
"price": { "ht": 209244, "ttc": 249000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 2092440, "discountHt": 0, "totalHt": 2092440, "vat": 397560, "totalTtc": 2490000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
},
{
"id": "01a1a0c0-4e80-7e8e-8f06-3842302674ed",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 20,
"price": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 2100840, "discountHt": 0, "totalHt": 2100840, "vat": 399160, "totalTtc": 2500000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
},
{
"id": "01a1a0c0-4e80-75c9-a6c9-7ca88b6f19f9",
"product": { "id": "01a15889-fa80-7ff3-b294-f863a2ee34a6", "name": "HOCO J86A Power Bank 10000mAh", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/j86a/j86a-black-1-240.webp" },
"variant": { "id": "01a15889-fe68-733d-8056-75eec8b226f5", "sku": "J86A_BLACK", "label": "Noir" },
"qty": 5,
"price": { "ht": 302521, "ttc": 360000, "vatRate": 1900 },
"promotionApplied": {
"promotionId": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17",
"label": "Offre flash -30 %",
"type": "PERCENT_OFF",
"value": 3000,
"unitPrice": { "ht": 211765, "ttc": 252000, "vatRate": 1900 },
"endsAt": "2026-11-03T22:59:59Z"
},
"lineTotals": { "subtotalHt": 1512605, "discountHt": 453780, "totalHt": 1058825, "vat": 201175, "totalTtc": 1260000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
}
],
"totals": { "subtotalHt": 7208405, "discountTotal": 453780, "deliveryFee": 0, "vatTotal": 1283375, "totalTtc": 8038000, "currency": "DZD" },
"displayTotals": { "itemsTtc": 8578000, "discountTtc": 540000, "deliveryFeeTtc": 0, "totalTtc": 8038000 },
"appliedPromotions": [
{ "promotionId": "01a19cfd-2c00-78b5-80c8-b4b9fb167f17", "label": "Offre flash -30 %", "type": "PERCENT_OFF", "lineIds": ["01a1a0c0-4e80-75c9-a6c9-7ca88b6f19f9"], "discountHt": 453780, "discountTtc": 540000 }
],
"address": {
"id": "01a12f63-efa8-7f36-87fc-e386a4e58a22",
"label": "Magasin",
"recipientName": "Mohamed Benali",
"phone": "+213555123456",
"wilayaCode": "31",
"wilayaName": "Oran",
"communeId": "01a0c3e1-5d20-7a10-8b31-310300000003",
"communeName": "Bir El Djir",
"addressLine": "Cité USTO, local n° 14",
"landmark": "En face de la pharmacie Benyahia",
"isDefault": true
},
"note": "Merci d'appeler avant la livraison, le magasin ferme à 13h le vendredi.",
"paymentMethod": "CASH_ON_DELIVERY",
"paymentStatus": { "invoiced": false, "invoiceId": null, "invoiceNumber": null, "invoiceStatus": null, "dueDate": null, "amountPaid": 0, "creditNotesTotal": 0, "balance": 0, "pendingVerificationAmount": 0 },
"delivery": { "status": null, "mode": null, "carrierName": null, "trackingNumber": null, "trackingUrl": null, "driverName": null, "driverPhone": null, "deliveryNoteNumber": null, "shippedAt": null, "deliveredAt": null, "recipientName": null },
"statusHistory": [
{ "from": null, "to": "SUBMITTED", "at": "2026-11-03T09:32:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" } },
{ "from": "SUBMITTED", "to": "CANCELLED", "at": "2026-11-03T09:41:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" }, "reason": "Commande en double — Commande passée deux fois, je garde CMD-2026-000157." }
],
"changes": [],
"documents": [
{ "type": "INVOICE", "number": null, "available": false, "generatedAt": null },
{ "type": "DELIVERY_NOTE", "number": null, "available": false, "generatedAt": null }
],
"assisted": { "isAssisted": false, "delegate": null, "confirmation": null, "expiresAt": null, "confirmedAt": null, "rejectionReason": null },
"cancellation": { "reasonCode": "DUPLICATE_ORDER", "reasonLabel": "Commande en double", "comment": "Commande passée deux fois, je garde CMD-2026-000157.", "at": "2026-11-03T09:41:00Z", "by": "RESELLER" },
"refusal": null,
"returns": [],
"claims": [],
"returnableUntil": null,
"claimableUntil": null,
"allowedActions": ["REORDER", "CONTACT_SUPPORT"]
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | reasonCode unknown, inactive or not open to resellers. |
| 404 | NOT_FOUND | Not my order. |
| 409 | ORDER_TRANSITION_NOT_ALLOWED | Status not in orders.cancelAllowedStatuses — typically already confirmed (« Commande déjà confirmée : contactez le support »), or an assisted order awaiting my confirmation (use reject). details: { from, to: "CANCELLED", allowed }. |
| 422 | REASON_REQUIRED | OTHER without comment. |
Rules & side effects
- Allowed only before the back-office confirmation:
orders.cancelAllowedStatusesacceptsSUBMITTEDandPENDINGonly (exposed to the apps byappConfig.get). After confirmation the reseller contacts support; HOCO can still cancel until shipping (admin.orders.cancel). - In one transaction: status
CANCELLED,OrderStatusChangewith the reason, reservations released automatically if any (stock.reserveOn = SUBMITTED), promotion caps given back, credit exposure released, first-order flag moved to the next order if this was the first (lifecycle). - Outbox
order.cancelled: the order leaves the confirmation queue,orders.validateholders get an in-app alert, the assigned delegate is informed; the reseller gets no push (own action) but the live event updates their other open screens.
Used by — « Annuler la commande » on RA-22 and RW-09.
POST /orders/{orderId}/confirm
orders.confirmAssisted · Access session — reseller (self), AccountStatus ACTIVE · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements ORD-08
Contract 1.1.0 (SRV-41) — RA-23: the body also accepts paymentMethod, addressId (one of my addresses; 404 NOT_FOUND otherwise, 422 ADDRESS_MISSING without an address line) and note (≤ 500, replaces the delegate's). They are written with the confirmation; the order is re-priced for the chosen address (delivery fee) and method (credit checks), so expectedTotalTtc is the total shown for them — a different total answers 409 CART_CHANGED with the re-priced order.
Purpose. The reseller confirms the order their delegate prepared for them (cahier §4.4 « Une commande assistée doit être confirmée par le revendeur ou validée par l'administration », §6.7). The order is then submitted to the back-office like any order.
Request
interface ConfirmAssistedRequest {
expectedTotalTtc: Money; // total shown on RA-23
}{ "expectedTotalTtc": 2495000 }Response — 200 OK: the OrderDetail, now SUBMITTED and waiting for the back-office.
{
"id": "01a1a0de-84e0-79ad-9ae7-a8167cc6fcaa",
"number": "CMD-2026-000159",
"version": 3,
"status": "SUBMITTED",
"progressStep": 0,
"awaitingConfirmation": "BACK_OFFICE",
"pendingReasons": [],
"channel": "DELEGATE_ASSISTED",
"isFirstOrder": false,
"createdAt": "2026-11-03T10:05:00Z",
"submittedAt": "2026-11-03T18:20:00Z",
"validatedAt": null,
"updatedAt": "2026-11-03T18:20:00Z",
"currency": "DZD",
"itemsCount": 2,
"unitsCount": 15,
"lines": [
{
"id": "01a1a0de-84e0-75c9-8cd5-80f3fe61a689",
"product": { "id": "01a08ac2-8900-7f6d-937c-c81b37e89a5b", "name": "HOCO N32 Chargeur rapide 30W (USB-C + USB-A)", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/n32/n32-black-1-240.webp" },
"variant": { "id": "01a08ac2-8ce8-7069-8328-28fd7158a572", "sku": "N32_BLACK_EU", "label": "Noir" },
"qty": 5,
"price": { "ht": 209244, "ttc": 249000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 1046220, "discountHt": 0, "totalHt": 1046220, "vat": 198780, "totalTtc": 1245000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
},
{
"id": "01a1a0de-84e0-721a-acd6-1f49ea0f9f86",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 10,
"price": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 1050420, "discountHt": 0, "totalHt": 1050420, "vat": 199580, "totalTtc": 1250000 },
"originalQty": null,
"returnedQty": 0,
"claimedQty": 0,
"returnableQty": 0
}
],
"totals": { "subtotalHt": 2096640, "discountTotal": 0, "deliveryFee": 0, "vatTotal": 398360, "totalTtc": 2495000, "currency": "DZD" },
"displayTotals": { "itemsTtc": 2495000, "discountTtc": 0, "deliveryFeeTtc": 0, "totalTtc": 2495000 },
"appliedPromotions": [],
"address": {
"id": "01a12f63-efa8-7f36-87fc-e386a4e58a22",
"label": "Magasin",
"recipientName": "Mohamed Benali",
"phone": "+213555123456",
"wilayaCode": "31",
"wilayaName": "Oran",
"communeId": "01a0c3e1-5d20-7a10-8b31-310300000003",
"communeName": "Bir El Djir",
"addressLine": "Cité USTO, local n° 14",
"landmark": "En face de la pharmacie Benyahia",
"isDefault": true
},
"note": "Préparée avec Yacine lors de la visite du 3 novembre.",
"paymentMethod": "CASH_ON_DELIVERY",
"paymentStatus": { "invoiced": false, "invoiceId": null, "invoiceNumber": null, "invoiceStatus": null, "dueDate": null, "amountPaid": 0, "creditNotesTotal": 0, "balance": 2495000, "pendingVerificationAmount": 0 },
"delivery": { "status": null, "mode": null, "carrierName": null, "trackingNumber": null, "trackingUrl": null, "driverName": null, "driverPhone": null, "deliveryNoteNumber": null, "shippedAt": null, "deliveredAt": null, "recipientName": null },
"statusHistory": [
{ "from": "DRAFT", "to": "PENDING", "at": "2026-11-03T10:05:00Z", "actor": { "id": "01a0f2b1-7d3c-7a45-9e61-0b2c3d4e5f60", "displayName": "Yacine Belkacem" }, "reason": "En attente de votre confirmation" },
{ "from": "PENDING", "to": "SUBMITTED", "at": "2026-11-03T18:20:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" } }
],
"changes": [],
"documents": [
{ "type": "INVOICE", "number": null, "available": false, "generatedAt": null },
{ "type": "DELIVERY_NOTE", "number": null, "available": false, "generatedAt": null }
],
"assisted": {
"isAssisted": true,
"delegate": { "id": "01a0f2b1-7d3c-7a45-9e61-0b2c3d4e5f60", "displayName": "Yacine Belkacem", "phone": "+213770123456" },
"confirmation": "CONFIRMED_BY_RESELLER",
"expiresAt": "2026-11-06T10:05:00Z",
"confirmedAt": "2026-11-03T18:20:00Z",
"rejectionReason": null
},
"cancellation": null,
"refusal": null,
"returns": [],
"claims": [],
"returnableUntil": null,
"claimableUntil": null,
"allowedActions": ["CANCEL", "CONTACT_SUPPORT"]
}Errors
| Status | Code | When |
|---|---|---|
| 403 | ACCOUNT_PENDING_VALIDATION | My account is not validated yet — the administration can confirm on my behalf (admin.orders.confirmAssisted). |
| 403 | RESELLER_SUSPENDED | Commercial block. |
| 404 | NOT_FOUND | Not my order. |
| 409 | ORDER_TRANSITION_NOT_ALLOWED | Not an assisted order awaiting my confirmation (already confirmed by the administration, rejected, cancelled). A second confirmation of an order I already confirmed returns 200 with the current detail instead (double tap from two devices). |
| 409 | ASSISTED_ORDER_EXPIRED | assisted.expiresAt has passed, even if the expiry job has not run yet (details.expiresAt). |
| 409 | CART_CHANGED | The re-priced total differs from expectedTotalTtc. details: { expectedTotalTtc, actualTotalTtc, order: OrderDetail } — the re-priced order is saved (version +1) and shown again; the reseller confirms with the new total and a new Idempotency-Key. |
| 422 | PRODUCT_UNAVAILABLE · STOCK_INSUFFICIENT · ORDER_MIN_AMOUNT_NOT_MET · CREDIT_LIMIT_EXCEEDED | Same controls as a checkout (lifecycle); details.issues. The reseller asks the delegate to adjust the order or rejects it. |
Rules & side effects
- The order is re-priced and re-controlled at confirmation exactly as a checkout (cahier §6.7 « Le prix, le stock et le crédit sont contrôlés comme pour une commande normale »);
expectedTotalTtcprotects the reseller from confirming a total they did not see. Assumption — honouring the price quoted by the delegate during the 72-hour window instead of re-pricing is an open question for HOCO. - In one transaction: confirmation
CONFIRMED_BY_RESELLER, statusPENDING→SUBMITTED(or staysPENDINGwithRESELLER_NOT_VALIDATED/CREDIT_OVER_LIMIT), promotion caps consumed, first-order flag and attribution to the source delegate (ORD-09) — then the same side effects ascart.checkout: the order joins the back-office confirmation queue. - Outbox
assisted_order.confirmed(push to the delegate) andorder.submitted.
Used by — « Confirmer la commande » on RA-23 and RW-09, reached from the push or the « À confirmer » pin.
POST /orders/{orderId}/reject
orders.rejectAssisted · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements ORD-08
Contract 1.1.0 (SRV-41): send reasonCode (a code of the ASSISTED_REJECT_REASON reference list: WRONG_ITEMS, TOO_EXPENSIVE, NOT_REQUESTED, LATER, OTHER) and an optional comment (≤ 300, required for OTHER); the stored reason is « label — comment ». The free-text reason stays accepted; one of the two is required (400 VALIDATION_FAILED, field reasonCode).
Purpose. The reseller declines the assisted order, with a reason the delegate will read.
Request
interface RejectAssistedRequest {
reason: string; // 3–300 characters
}{ "reason": "Je préfère commander moi-même la semaine prochaine, après mon inventaire." }Response — 200 OK: the OrderDetail with status: "CANCELLED", assisted.confirmation: "REJECTED_BY_RESELLER", assisted.rejectionReason set, cancellation: { "reasonCode": "REJECTED_BY_RESELLER", "reasonLabel": "Refusée par le revendeur", "by": "RESELLER", … } and allowedActions: ["REORDER", "CONTACT_SUPPORT"]. Excerpt of what changes from the confirmation example above:
{
"id": "01a1a0de-84e0-79ad-9ae7-a8167cc6fcaa",
"number": "CMD-2026-000159",
"version": 3,
"status": "CANCELLED",
"progressStep": null,
"awaitingConfirmation": null,
"statusHistory": [
{ "from": "DRAFT", "to": "PENDING", "at": "2026-11-03T10:05:00Z", "actor": { "id": "01a0f2b1-7d3c-7a45-9e61-0b2c3d4e5f60", "displayName": "Yacine Belkacem" }, "reason": "En attente de votre confirmation" },
{ "from": "PENDING", "to": "CANCELLED", "at": "2026-11-03T18:20:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" }, "reason": "Refusée par le revendeur — Je préfère commander moi-même la semaine prochaine, après mon inventaire." }
],
"assisted": {
"isAssisted": true,
"delegate": { "id": "01a0f2b1-7d3c-7a45-9e61-0b2c3d4e5f60", "displayName": "Yacine Belkacem", "phone": "+213770123456" },
"confirmation": "REJECTED_BY_RESELLER",
"expiresAt": "2026-11-06T10:05:00Z",
"confirmedAt": null,
"rejectionReason": "Je préfère commander moi-même la semaine prochaine, après mon inventaire."
},
"cancellation": {
"reasonCode": "REJECTED_BY_RESELLER",
"reasonLabel": "Refusée par le revendeur",
"comment": "Je préfère commander moi-même la semaine prochaine, après mon inventaire.",
"at": "2026-11-03T18:20:00Z",
"by": "RESELLER"
},
"allowedActions": ["REORDER", "CONTACT_SUPPORT"]
}All other fields keep the values shown in the confirmation example (the full OrderDetail is always returned).
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | reason shorter than 3 or longer than 300 characters. |
| 404 | NOT_FOUND | Not my order. |
| 409 | ORDER_TRANSITION_NOT_ALLOWED | Not awaiting my confirmation. |
| 409 | ASSISTED_ORDER_EXPIRED | Already expired (it is cancelled by the job anyway). |
Rules & side effects — status CANCELLED with the system reason « Refusée par le revendeur » (locked ORDER_CANCEL_REASON item); nothing was reserved or consumed, so there is nothing to release; outbox assisted_order.rejected → push to the delegate with the reason (DA-18). The delegate may prepare a new assisted order afterwards.
Used by — « Refuser » on RA-23 and RW-09.
POST /orders/{orderId}/reorder
orders.reorder · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements ORD-07
Purpose. « Recommander » (cahier §5.4): copies the lines of a past order into my cart at today's prices and tells me which lines could not be copied.
Request
interface ReorderRequest {
mode?: "MERGE" | "REPLACE"; // default MERGE: add to the current cart; REPLACE empties it first
}{ "mode": "MERGE" }Response — 200 OK.
interface ReorderResult {
cart: CartView; // see the cart page
addedLines: number;
unavailableLines: { orderItemId: Uuid; sku: string; productName: string; qty: number;
reason: "PRODUCT_UNAVAILABLE" | "OUT_OF_STOCK" }[];
adjustedLines: { orderItemId: Uuid; sku: string; productName: string; fromQty: number; toQty: number;
reason: "QUANTITY_RULES" }[];
}Example — reordering CMD-2026-000154 (delivered: X89 × 20, A45 × 3, CA91 × 10) into an empty cart; the CA91 car holder has been archived since.
{
"cart": {
"id": "01a1396c-e400-70fe-bda9-3dc62fcad676",
"updatedAt": "2026-11-05T08:10:00Z",
"itemsCount": 2,
"unitsCount": 23,
"lines": [
{
"id": "01a1aac1-f3c0-7b47-9126-75c245282858",
"kind": "VARIANT",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "brandName": "HOCO", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 20,
"limits": { "minQty": 5, "qtyStep": 5, "maxQtyPerOrder": 200 },
"price": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 2100840, "discountHt": 0, "totalHt": 2100840, "vat": 399160, "totalTtc": 2500000 },
"availability": "IN_STOCK",
"addedAt": "2026-11-05T08:10:00Z",
"issues": []
},
{
"id": "01a1aac1-f3c0-73a0-af10-be1da58e9e4b",
"kind": "VARIANT",
"product": { "id": "01a08fe8-e500-7017-b062-03c53547a2be", "name": "ACEFAST A45 Chargeur rapide", "brandName": "ACEFAST", "imageUrl": "https://cdn.hoco.dz/products/a45/a45-black-1-240.webp" },
"variant": { "id": "01a08fe8-e8e8-762d-81d1-ab0daa011e53", "sku": "A45_BLACK_EU", "label": "Noir" },
"qty": 3,
"limits": { "minQty": 1, "qtyStep": 1, "maxQtyPerOrder": 30 },
"price": { "ht": 331933, "ttc": 395000, "vatRate": 1900 },
"promotionApplied": null,
"lineTotals": { "subtotalHt": 995799, "discountHt": 0, "totalHt": 995799, "vat": 189201, "totalTtc": 1185000 },
"availability": "IN_STOCK",
"addedAt": "2026-11-05T08:10:00Z",
"issues": []
}
],
"totals": { "subtotalHt": 3096639, "discountTotal": 0, "deliveryFee": 0, "vatTotal": 588361, "totalTtc": 3685000, "currency": "DZD" },
"displayTotals": { "itemsTtc": 3685000, "discountTtc": 0, "deliveryFeeTtc": 0, "totalTtc": 3685000 },
"appliedPromotions": [],
"delivery": { "addressId": "01a12f63-efa8-7f36-87fc-e386a4e58a22", "feePolicy": "FREE", "isFree": true, "freeThresholdTtc": null },
"minimumOrder": { "amountTtc": 1000000, "met": true, "missingTtc": 0 },
"issues": [],
"canCheckout": true
},
"addedLines": 2,
"unavailableLines": [
{ "orderItemId": "01a17cd2-00e0-7c68-a5a6-21eea8b7a786", "sku": "CA91_BLACK", "productName": "HOCO CA91 Support voiture magnétique", "qty": 10, "reason": "PRODUCT_UNAVAILABLE" }
],
"adjustedLines": []
}Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Not my order. |
| 409 | ORDER_TRANSITION_NOT_ALLOWED | Status not DELIVERED, CANCELLED, REFUSED or RETURNED (details.allowed). |
| 422 | CART_LINE_LIMIT_REACHED | The merged cart would exceed 100 lines; nothing is added. |
| 422 | NOTHING_TO_REORDER | No line of the order can be added (all unavailable); the cart is unchanged and details.unavailableLines lists why. |
Rules & side effects
- Each order line is added like
cart.items.addwith the order's quantity, at today's price for my customer type and today's promotions. - Lines whose product is no longer visible or orderable, or whose variant is out of stock, are skipped and reported in
unavailableLines. A quantity that no longer fits the variant's rules (minimum, step, maximum) is moved to the nearest allowed quantity and reported inadjustedLines. - A line in stock whose full quantity is not available is still added, with its quantity, and carries the blocking
STOCK_INSUFFICIENTissue in the returned cart (no quantity is revealed); the reseller lowers it in the cart. - The client opens the cart (RA-17) and shows the reported lines in a banner.
Used by — « Recommander » on RA-21, RA-22, RW-08, RW-09.
GET /orders/{orderId}/documents/{type}
orders.document · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-12, FIN-01
Purpose. « Télécharger la facture » (reference screen 6) or the delivery note: returns a short-lived signed link to the PDF.
Request
| Path / query | Type | Required | Notes |
|---|---|---|---|
orderId | Uuid | yes | |
type | INVOICE | DELIVERY_NOTE | yes | |
download | boolean | no | true → Content-Disposition: attachment (web download); default false (in-app viewer) |
GET /v1/orders/01a187cf-6b40-7c6f-894d-b1f256947da9/documents/INVOICE?download=trueResponse — 200 OK, Cache-Control: no-store.
{
"type": "INVOICE",
"number": "FAC-2026-000124",
"fileName": "FAC-2026-000124_Magasin_El_Wafa.pdf",
"file": {
"id": "01a18d53-40f0-7e81-8297-383013436902",
"url": "https://storage.hoco.dz/hoco-private/generated_document/2026/10/01a18d53-40f0-7e81-8297-383013436902.pdf?X-Amz-Expires=300&response-content-disposition=attachment%3B%20filename%3D%22FAC-2026-000124_Magasin_El_Wafa.pdf%22&X-Amz-Signature=5be1…",
"mimeType": "application/pdf",
"size": 48213,
"purpose": "GENERATED_DOCUMENT"
},
"expiresAt": "2026-11-03T09:35:00Z",
"generatedAt": "2026-10-30T15:05:10Z"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | type not INVOICE or DELIVERY_NOTE. |
| 404 | NOT_FOUND | Not my order. |
| 404 | DOCUMENT_NOT_AVAILABLE | Not generated yet: the delivery note exists from READY, the invoice from billing.invoiceOn (default SHIPPED); or the invoice was cancelled (details.reason: NOT_GENERATED, CANCELLED). |
Rules & side effects — the URL is valid 5 minutes (files) and each issued URL is logged with user and time; the PDF is the latest version (a regeneration never changes amounts). Credit notes are reached from returns and invoices.
Used by — RA-22 (« Télécharger la facture », « Bon de livraison »), RW-09, and the invoice screens RA-30, RW-13.
POST /orders/{orderId}/returns
returns.create · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements ORD-13, STK-03
Contract 1.1.0 (SRV-41): send reasonCode (a code of the RETURN_REASON reference list: DAMAGED_ON_DELIVERY, DEFECTIVE, WRONG_ITEM, NOT_AS_DESCRIBED, OVERSTOCK, OTHER) and reason as an optional detail (required for OTHER), or reason alone (≥ 10 characters) as before. The return keeps the code in reasonCode (ReturnView) and « label — detail » in reason.
Purpose. « Demander un retour »: asks HOCO to take back items of a delivered order (wrong, damaged or defective items noticed after delivery), with photos.
Request
interface CreateReturnRequest {
reason: string; // 10–500 characters, shown to the back-office
items: {
orderItemId: Uuid; // OrderDetail.lines[].id
qty: number; // 1 … returnableQty
photoFileIds: Uuid[]; // 0–4 TICKET_ATTACHMENT files per item; at least one photo per request
}[]; // 1–50 items, each orderItemId once
}{
"reason": "Deux câbles X89 arrivés coupés dans le carton et un chargeur A45 qui ne s'allume pas.",
"items": [
{ "orderItemId": "01a17cd2-00e0-7829-8228-5952ab20d8f4", "qty": 2, "photoFileIds": ["01a1a0f4-2b10-7c31-9d42-5e6f708192a3"] },
{ "orderItemId": "01a17cd2-00e0-7e2c-ad45-104861602e5d", "qty": 1, "photoFileIds": ["01a1a0f4-3c21-7d42-8e53-6f708192a3b4"] }
]
}Response — 201 Created, Location: /v1/returns/01a1a0f6-1a00-7b5c-8d6e-7f8091a2b3c4: a ReturnView in REQUESTED.
{
"id": "01a1a0f6-1a00-7b5c-8d6e-7f8091a2b3c4",
"number": "RET-2026-000012",
"status": "REQUESTED",
"origin": "RESELLER",
"order": { "id": "01a17cd2-00e0-7779-8e81-ceca0079d9c9", "number": "CMD-2026-000154", "deliveredAt": "2026-10-28T11:40:00Z" },
"ticket": null,
"reason": "Deux câbles X89 arrivés coupés dans le carton et un chargeur A45 qui ne s'allume pas.",
"items": [
{
"id": "01a1a0f6-1a00-7c11-9a22-0b1c2d3e4f50",
"orderItemId": "01a17cd2-00e0-7829-8228-5952ab20d8f4",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 2,
"unitPrice": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"amountTtc": 250000,
"photos": [
{ "id": "01a1a0f4-2b10-7c31-9d42-5e6f708192a3", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-2b10-7c31-9d42-5e6f708192a3.webp?X-Amz-Expires=300&X-Amz-Signature=91ac…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-2b10-7c31-9d42-5e6f708192a3-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=02bd…", "mimeType": "image/webp", "size": 214876, "purpose": "TICKET_ATTACHMENT" }
],
"receivedQty": null,
"acceptedQty": null,
"refusalReason": null
},
{
"id": "01a1a0f6-1a00-7d22-8b33-1c2d3e4f5061",
"orderItemId": "01a17cd2-00e0-7e2c-ad45-104861602e5d",
"product": { "id": "01a08fe8-e500-7017-b062-03c53547a2be", "name": "ACEFAST A45 Chargeur rapide", "imageUrl": "https://cdn.hoco.dz/products/a45/a45-black-1-240.webp" },
"variant": { "id": "01a08fe8-e8e8-762d-81d1-ab0daa011e53", "sku": "A45_BLACK_EU", "label": "Noir" },
"qty": 1,
"unitPrice": { "ht": 331933, "ttc": 395000, "vatRate": 1900 },
"amountTtc": 395000,
"photos": [
{ "id": "01a1a0f4-3c21-7d42-8e53-6f708192a3b4", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-3c21-7d42-8e53-6f708192a3b4.webp?X-Amz-Expires=300&X-Amz-Signature=7d10…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-3c21-7d42-8e53-6f708192a3b4-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=c4e2…", "mimeType": "image/webp", "size": 187340, "purpose": "TICKET_ATTACHMENT" }
],
"receivedQty": null,
"acceptedQty": null,
"refusalReason": null
}
],
"amountTtc": 645000,
"instructions": null,
"transport": null,
"rejectionReason": null,
"creditNote": null,
"statusHistory": [
{ "from": null, "to": "REQUESTED", "at": "2026-11-03T15:20:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" } }
],
"requestedAt": "2026-11-03T15:20:00Z",
"updatedAt": "2026-11-03T15:20:00Z"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Field codes: TOO_SHORT (reason), PHOTO_REQUIRED (no photo in the whole request), TOO_MANY (more than 4 photos on an item), DUPLICATE_ITEM, UNKNOWN_ORDER_ITEM (not a line of this order), FILE_NOT_FOUND (unknown or not uploaded by me), FILE_ALREADY_ATTACHED. |
| 403 | RESELLER_SUSPENDED | Commercial block. |
| 404 | NOT_FOUND | Not my order. |
| 422 | FILE_NOT_READY | A photo's upload was not completed (files). |
| 422 | FILE_PURPOSE_MISMATCH | A photo was not uploaded with purpose TICKET_ATTACHMENT. |
| 409 | ORDER_NOT_DELIVERED | The order is not DELIVERED (details.status). |
| 422 | RETURN_WINDOW_EXPIRED | More than returns.windowDays (default 7) after deliveredAt (details.returnableUntil). For warranty problems the app offers an after-sales claim instead (OPEN_CLAIM). |
| 422 | RETURN_QTY_EXCEEDS_DELIVERED | qty above the line's returnableQty (delivered − already returned − already claimed), details.lines: [{ orderItemId, returnableQty }]. |
Rules & side effects
ReturnRequest+ReturnItemrows, numberRET-YYYY-NNNNNN, statusREQUESTED, originRESELLER; the photos are attached (they become readable by staff withorders.view).- Nothing moves in stock or finance yet: the back-office approves or rejects (
admin.returns.decide), sets the transport andinstructions, and at reception (admin.returns.receive) the accepted quantities re-enter stock automatically and a credit note is issued (lifecycle, FIN-08). - Outbox
return.requested: SSE and in-app alert toorders.validateholders (returns queue BO-69), acknowledgement in the reseller's inbox; every later status change reaches the reseller live (return.status_changed) and by push. - The order keeps
DELIVERED; it becomesRETURNEDonly when every delivered unit has come back.
Used by — RA-40 (« Demande de retour », from « Demander un retour » on RA-22), RW-14.
GET /returns
returns.list · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-13
Purpose. My return requests with their status, newest first.
Request
| Query | Type | Required | Notes |
|---|---|---|---|
status | ReturnStatus, repeatable | no | |
orderId | Uuid | no | Returns of one order |
cursor, limit | — | no | limit default 20, max 100 |
Response — 200 OK: { items: ReturnSummary[], nextCursor } where ReturnSummary = { id, number, status, origin, order { id, number }, itemsCount, unitsCount, amountTtc, creditNote { number, amountTtc } | null, requestedAt, updatedAt }.
{
"items": [
{
"id": "01a1a0f6-1a00-7b5c-8d6e-7f8091a2b3c4",
"number": "RET-2026-000012",
"status": "REFUNDED",
"origin": "RESELLER",
"order": { "id": "01a17cd2-00e0-7779-8e81-ceca0079d9c9", "number": "CMD-2026-000154" },
"itemsCount": 2,
"unitsCount": 3,
"amountTtc": 645000,
"creditNote": { "number": "AV-2026-000004", "amountTtc": 645000 },
"requestedAt": "2026-11-03T15:20:00Z",
"updatedAt": "2026-11-06T10:30:00Z"
},
{
"id": "01a10f2a-6c00-7e41-9b52-3d4e5f607182",
"number": "RET-2026-000009",
"status": "REJECTED",
"origin": "RESELLER",
"order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131" },
"itemsCount": 1,
"unitsCount": 8,
"amountTtc": 2304000,
"creditNote": null,
"requestedAt": "2026-10-09T10:12:00Z",
"updatedAt": "2026-10-09T15:40:00Z"
}
],
"nextCursor": null
}Errors — 400 VALIDATION_FAILED for an unknown status.
Rules & side effects — read-only; includes returns created by an after-sales resolution (origin: AFTER_SALES).
Used by — returns section of RA-40 and of RW-14; returns block of RA-22.
GET /returns/{returnId}
returns.get · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements ORD-13, FIN-08
Purpose. A return with its lines, the instructions to send the goods back, the inspection result, the status history and the credit note.
Request — path returnId.
Response — 200 OK: a ReturnView. Example: RET-2026-000012 three days later — collected by the delegate, inspected, credited.
{
"id": "01a1a0f6-1a00-7b5c-8d6e-7f8091a2b3c4",
"number": "RET-2026-000012",
"status": "REFUNDED",
"origin": "RESELLER",
"order": { "id": "01a17cd2-00e0-7779-8e81-ceca0079d9c9", "number": "CMD-2026-000154", "deliveredAt": "2026-10-28T11:40:00Z" },
"ticket": null,
"reason": "Deux câbles X89 arrivés coupés dans le carton et un chargeur A45 qui ne s'allume pas.",
"items": [
{
"id": "01a1a0f6-1a00-7c11-9a22-0b1c2d3e4f50",
"orderItemId": "01a17cd2-00e0-7829-8228-5952ab20d8f4",
"product": { "id": "01a08ac7-1ce0-7b52-8b6a-3ec2aaccb8fb", "name": "HOCO X89 Câble USB-C", "imageUrl": "https://cdn.hoco.dz/products/x89/x89-black-1-240.webp" },
"variant": { "id": "01a08ac7-20c8-70bf-b753-d181dfe71ff6", "sku": "X89_BLACK_1M", "label": "Noir · 1 m" },
"qty": 2,
"unitPrice": { "ht": 105042, "ttc": 125000, "vatRate": 1900 },
"amountTtc": 250000,
"photos": [
{ "id": "01a1a0f4-2b10-7c31-9d42-5e6f708192a3", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-2b10-7c31-9d42-5e6f708192a3.webp?X-Amz-Expires=300&X-Amz-Signature=3f8e…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-2b10-7c31-9d42-5e6f708192a3-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=a6b0…", "mimeType": "image/webp", "size": 214876, "purpose": "TICKET_ATTACHMENT" }
],
"receivedQty": 2,
"acceptedQty": 2,
"refusalReason": null
},
{
"id": "01a1a0f6-1a00-7d22-8b33-1c2d3e4f5061",
"orderItemId": "01a17cd2-00e0-7e2c-ad45-104861602e5d",
"product": { "id": "01a08fe8-e500-7017-b062-03c53547a2be", "name": "ACEFAST A45 Chargeur rapide", "imageUrl": "https://cdn.hoco.dz/products/a45/a45-black-1-240.webp" },
"variant": { "id": "01a08fe8-e8e8-762d-81d1-ab0daa011e53", "sku": "A45_BLACK_EU", "label": "Noir" },
"qty": 1,
"unitPrice": { "ht": 331933, "ttc": 395000, "vatRate": 1900 },
"amountTtc": 395000,
"photos": [
{ "id": "01a1a0f4-3c21-7d42-8e53-6f708192a3b4", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-3c21-7d42-8e53-6f708192a3b4.webp?X-Amz-Expires=300&X-Amz-Signature=b21d…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a0f4-3c21-7d42-8e53-6f708192a3b4-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=e95c…", "mimeType": "image/webp", "size": 187340, "purpose": "TICKET_ATTACHMENT" }
],
"receivedQty": 1,
"acceptedQty": 1,
"refusalReason": null
}
],
"amountTtc": 645000,
"instructions": "Votre délégué Yacine récupère les articles lors de sa visite du jeudi 5 novembre. Gardez-les dans leur emballage d'origine.",
"transport": { "mode": "DELEGATE_PICKUP", "scheduledFor": "2026-11-05", "trackingNumber": null },
"rejectionReason": null,
"creditNote": {
"id": "01a1a8c2-5e00-7a61-8b72-9c0d1e2f3a4b",
"number": "AV-2026-000004",
"amountTtc": 645000,
"issuedAt": "2026-11-06T10:30:00Z",
"file": {
"id": "01a1a8c2-6f11-7b72-9c83-0d1e2f3a4b5c",
"url": "https://storage.hoco.dz/hoco-private/generated_document/2026/11/01a1a8c2-6f11-7b72-9c83-0d1e2f3a4b5c.pdf?X-Amz-Expires=300&X-Amz-Signature=d4a7…",
"mimeType": "application/pdf",
"size": 39804,
"purpose": "GENERATED_DOCUMENT"
}
},
"statusHistory": [
{ "from": null, "to": "REQUESTED", "at": "2026-11-03T15:20:00Z", "actor": { "id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57", "displayName": "Mohamed Benali" } },
{ "from": "REQUESTED", "to": "APPROVED", "at": "2026-11-04T09:10:00Z", "actor": { "id": "01a05bfb-7000-7d71-b777-8df1872390e6", "displayName": "Équipe HOCO" } },
{ "from": "APPROVED", "to": "IN_TRANSIT", "at": "2026-11-05T11:25:00Z", "actor": { "id": "01a05bfb-77d0-76d5-a064-865b3b84647b", "displayName": "Équipe HOCO" } },
{ "from": "IN_TRANSIT", "to": "RECEIVED", "at": "2026-11-06T10:30:00Z", "actor": { "id": "01a05bfb-77d0-76d5-a064-865b3b84647b", "displayName": "Équipe HOCO" } },
{ "from": "RECEIVED", "to": "REFUNDED", "at": "2026-11-06T10:30:00Z", "actor": null, "reason": "Avoir AV-2026-000004 émis" }
],
"requestedAt": "2026-11-03T15:20:00Z",
"updatedAt": "2026-11-06T10:30:00Z"
}| Field | Notes |
|---|---|
instructions, transport | Set at approval: drop at the depot (RESELLER_DROP_OFF), carrier collection with a tracking number, or the delegate's next visit (DELEGATE_PICKUP). |
items[].acceptedQty | Units credited; units not accepted carry refusalReason (« Produit utilisé, emballage manquant ») and are not re-entered. |
creditNote | Number AV-YYYY-NNNNNN (assumption, format to confirm in D-21); amount = accepted units × the order line's net unit price TTC. It reduces the invoice balance, or becomes a credit balance when the invoice is already paid (finance). |
Errors
| Status | Code | When |
|---|---|---|
| 404 | NOT_FOUND | Not my return. |
Rules & side effects — read-only; photo and PDF URLs are signed for 5 minutes (refetch to renew). Live updates through return.status_changed.
Cart & checkout
The reseller's server-side cart shared by the app and the web — lines, re-validated prices and availability, promotions, totals, blocking issues — and the idempotent checkout that submits an order for back-office confirmation.
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.