HOCO B2B
SRSAPI reference

Support tickets

Reseller support: tickets and after-sales (SAV) claims with their conversation and resolution, support contacts and FAQ, and the regional WhatsApp number resolved from the account or the device location.

The « Assistance » area of the reseller app (Imad, MRA-08) and the support pages of the reseller web (Anis, WEB-05); built in SRV-24. The back-office side is support and communication.

  • Tickets carry a type, a subject, a conversation and attachments, optionally linked to one of my orders (SUP-01). Only public messages reach the reseller; internal notes never do.
  • After-sales claims are tickets of type AFTER_SALES (V1 — SUP-04): the order, the claimed lines with quantities and the issue (AfterSalesIssue), photos, within the after-sales window. The back-office diagnoses and applies a resolution — REPLACEMENT (a free order), RETURN (a return request), CREDIT_NOTE, REPAIR or REJECTED with a reason — which is shown on the ticket (ticket lifecycle).
  • Contacts and WhatsApp. support.contacts gives the phone, e-mail, hours and FAQ (SUP-03); support.whatsapp gives the WhatsApp number of the caller's support region — from the account's wilaya when signed in, else from the device location or a chosen wilaya — for the fixed WhatsApp button of both reseller apps (SUP-05, ADM-08). It works for guests.
MethodPathOperationAccessAppsRelease
GET/ticketstickets.list
My support tickets
selfRA · RWV1
POST/ticketstickets.create
Open a ticket (type, subject, message, related order, attachments)
self
Idempotency-Key
RA · RWV1
GET/tickets/{ticketId}tickets.get
Ticket with its public messages
selfRA · RWV1
POST/tickets/{ticketId}/messagestickets.reply
Reply to a ticket
self
Idempotency-Key
RA · RWV1
POST/tickets/{ticketId}/closetickets.close
Close my ticket (optionally rate the answer)
selfRA · RWV1
GET/support/contactssupport.contacts
Support phone, hours and FAQ entries
sessionRA · RWV1
GET/support/whatsappsupport.whatsapp
WhatsApp support number of the caller's region: declared region when signed in, else from lat/lng or wilayaCode; returns number, wa.me link and hours
guest or sessionRA · RWV1

Every operation can also answer 426 UPGRADE_REQUIRED, 429 RATE_LIMITED, 500 INTERNAL_ERROR and 503 MAINTENANCE; session operations also 401 UNAUTHENTICATED, 403 ACCOUNT_SUSPENDED and 403 PERMISSION_DENIED (caller is not a reseller); Idempotency-Key required operations also 400 IDEMPOTENCY_KEY_REQUIRED, 409 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_IN_PROGRESS. See the error registry.

Ticket types and what they require

TicketTypeLabelorderIdExtra requirements
AFTER_SALESService après-vente (SAV)required, order DELIVEREDitems (lines, quantities, AfterSalesIssue), at least one photo, within the after-sales window (BR-81: afterSales.windowDays after delivery, or the product warranty — D-35)
ORDER_ISSUEProblème de commanderequired—
DELIVERY_ISSUEProblème de livraisonrequired—
BILLINGFacturationoptional—
QUESTIONQuestionoptional—
OTHERAutreoptional—

Return or claim? A wrong, damaged or defective item noticed within returns.windowDays (7 days) of delivery can be returned directly (returns.create); afterwards — or when the reseller wants a replacement or a repair — it is an after-sales claim. The order detail tells the app which buttons to show (REQUEST_RETURN, OPEN_CLAIM in allowedActions).

Schemas

interface TicketSummary {
  id: Uuid;
  number: string;                     // TKT-2026-000046
  type: TicketType;
  subject: string;
  status: TicketStatus;               // OPEN, IN_PROGRESS, WAITING_RESELLER, RESOLVED, REJECTED, CLOSED
  order: { id: Uuid; number: string } | null;
  afterSalesResolution: AfterSalesResolution | null;
  lastMessageAt: Iso;
  lastMessagePreview: string;         // first 120 characters
  unreadCount: number;                // support messages I have not read
  rating: { score: number; comment: string | null; ratedAt: Iso } | null;
  createdAt: Iso;
  resolvedAt: Iso | null;
  closedAt: Iso | null;
}

interface TicketDetail extends TicketSummary {
  messages: TicketMessage[];          // public messages, oldest first
  afterSales: AfterSalesClaim | null; // AFTER_SALES only
  allowedActions: ("REPLY" | "CLOSE" | "RATE")[];
}

interface TicketMessage {
  id: Uuid;
  author: { type: "RESELLER" | "SUPPORT" | "SYSTEM"; displayName: string };   // support agents: first name + « Service client »
  body: string;
  attachments: FileRef[];             // TICKET_ATTACHMENT, signed URLs
  createdAt: Iso;
}

interface AfterSalesClaim {
  order: { id: Uuid; number: string; deliveredAt: Iso };
  items: {
    id: Uuid;                         // TicketItem id
    orderItemId: Uuid;
    product: { id: Uuid; name: string; imageUrl: string | null };
    variant: { id: Uuid; sku: string; label: string };
    qty: number;
    issue: AfterSalesIssue;           // DEFECTIVE, DAMAGED, WRONG_ITEM, MISSING_ITEM, NOT_AS_DESCRIBED, OTHER
  }[];
  claimableUntil: Iso;
  resolution: {
    type: AfterSalesResolution;       // REPLACEMENT, RETURN, CREDIT_NOTE, REPAIR, REJECTED
    note: string | null;              // explanation written for the reseller
    decidedAt: Iso;
    replacementOrder: { id: Uuid; number: string; status: OrderStatus } | null;
    return: { id: Uuid; number: string; status: ReturnStatus } | null;
    creditNote: { id: Uuid; number: string; amountTtc: Money } | null;
    repair: { expectedOn: string | null } | null;   // YYYY-MM-DD
    rejectionReason: string | null;
  } | null;
}

Operations

GET /tickets

tickets.list · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements SUP-01

Purpose. « Assistance »: my tickets and claims, the most recently active first.

Request

QueryTypeRequiredNotes
statusTicketStatus, repeatablenoTab En cours = OPEN, IN_PROGRESS, WAITING_RESELLER
typeTicketType, repeatableno
orderIdUuidnoTickets of one order
cursor, limit—nolimit default 20, max 100

Response — 200 OK: { items: TicketSummary[], nextCursor }.

{
  "items": [
    {
      "id": "01a1a12c-56c0-7989-951b-4022807290b6",
      "number": "TKT-2026-000046",
      "type": "AFTER_SALES",
      "subject": "BOROFONE BW06 — boîtier de charge défectueux",
      "status": "RESOLVED",
      "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131" },
      "afterSalesResolution": "REPLACEMENT",
      "lastMessageAt": "2026-11-05T09:40:00Z",
      "lastMessagePreview": "Réclamation résolue : remplacement. La commande CMD-2026-000163 a été créée pour vous.",
      "unreadCount": 1,
      "rating": null,
      "createdAt": "2026-11-03T11:30:00Z",
      "resolvedAt": "2026-11-05T09:40:00Z",
      "closedAt": null
    },
    {
      "id": "01a1965f-7240-7a21-a8d4-11ef017fd953",
      "number": "TKT-2026-000045",
      "type": "DELIVERY_ISSUE",
      "subject": "Commande CMD-2026-000155 pas encore reçue",
      "status": "WAITING_RESELLER",
      "order": { "id": "01a187cf-6b40-7c6f-894d-b1f256947da9", "number": "CMD-2026-000155" },
      "afterSalesResolution": null,
      "lastMessageAt": "2026-11-03T08:55:00Z",
      "lastMessagePreview": "Le transporteur indique un passage hier à 16h. Pouvez-vous nous confirmer si le magasin était ouvert ?",
      "unreadCount": 1,
      "rating": null,
      "createdAt": "2026-11-01T09:10:00Z",
      "resolvedAt": null,
      "closedAt": null
    },
    {
      "id": "01a15ec2-ff00-764a-b2a4-ffdae7dba695",
      "number": "TKT-2026-000041",
      "type": "BILLING",
      "subject": "Échéance de la facture FAC-2026-000079",
      "status": "CLOSED",
      "order": null,
      "afterSalesResolution": null,
      "lastMessageAt": "2026-10-22T09:15:00Z",
      "lastMessagePreview": "L'échéance reste au 28 octobre ; vous pouvez régler en deux fois.",
      "unreadCount": 0,
      "rating": { "score": 5, "comment": "Réponse rapide, merci.", "ratedAt": "2026-10-22T10:02:00Z" },
      "createdAt": "2026-10-21T14:00:00Z",
      "resolvedAt": "2026-10-22T09:15:00Z",
      "closedAt": "2026-10-22T10:02:00Z"
    }
  ],
  "nextCursor": null
}

Errors — 400 VALIDATION_FAILED for unknown filter values.

Rules & side effects — read-only, sorted by lastMessageAt descending. The SUPPORT quick tile of the home shows the number of tickets in WAITING_RESELLER. Live: ticket.replied and ticket.resolved on events.stream update the list.

Used by — RA-32 (« Assistance »), RW-14, claims block of RA-22.

POST /tickets

tickets.create · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements SUP-01, SUP-04

Purpose. « Nouvelle demande »: opens a ticket — a question, an order or delivery problem, a billing question, or an after-sales claim on delivered items.

Request — attachments are uploaded first with purpose TICKET_ATTACHMENT (files).

interface CreateTicketRequest {
  type: TicketType;
  subject: string;                    // 5–120 characters
  message: string;                    // 1–2 000 characters
  orderId?: Uuid;                     // required for AFTER_SALES, ORDER_ISSUE, DELIVERY_ISSUE
  attachmentFileIds?: Uuid[];         // 0–5; AFTER_SALES: at least 1 photo
  items?: {                           // AFTER_SALES only, 1–50 entries
    orderItemId: Uuid;                // OrderDetail.lines[].id
    qty: number;                      // ≤ delivered − already claimed − already returned
    issue: AfterSalesIssue;
  }[];
}
{
  "type": "AFTER_SALES",
  "subject": "BOROFONE BW06 — boîtier de charge défectueux",
  "message": "Trois paires BW06 de la commande CMD-2026-000131 : le boîtier ne charge plus les écouteurs après une semaine. Clients revenus au magasin.",
  "orderId": "01a1065b-2900-748c-88c5-976d443033f4",
  "attachmentFileIds": ["01a1a12a-8200-79a8-93c8-c5b89aac7ecf"],
  "items": [
    { "orderItemId": "01a1065b-2900-7a11-9b22-3c4d5e6f7a81", "qty": 3, "issue": "DEFECTIVE" }
  ]
}

Response — 201 Created, Location: /v1/tickets/01a1a12c-56c0-7989-951b-4022807290b6: the TicketDetail, status OPEN.

{
  "id": "01a1a12c-56c0-7989-951b-4022807290b6",
  "number": "TKT-2026-000046",
  "type": "AFTER_SALES",
  "subject": "BOROFONE BW06 — boîtier de charge défectueux",
  "status": "OPEN",
  "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131" },
  "afterSalesResolution": null,
  "lastMessageAt": "2026-11-03T11:30:00Z",
  "lastMessagePreview": "Trois paires BW06 de la commande CMD-2026-000131 : le boîtier ne charge plus les écouteurs après une semaine.",
  "unreadCount": 0,
  "rating": null,
  "createdAt": "2026-11-03T11:30:00Z",
  "resolvedAt": null,
  "closedAt": null,
  "messages": [
    {
      "id": "01a1a12c-56c0-7090-99d0-d51e9b32f39a",
      "author": { "type": "RESELLER", "displayName": "Mohamed Benali" },
      "body": "Trois paires BW06 de la commande CMD-2026-000131 : le boîtier ne charge plus les écouteurs après une semaine. Clients revenus au magasin.",
      "attachments": [
        { "id": "01a1a12a-8200-79a8-93c8-c5b89aac7ecf", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a12a-8200-79a8-93c8-c5b89aac7ecf.webp?X-Amz-Expires=300&X-Amz-Signature=2a9f…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a12a-8200-79a8-93c8-c5b89aac7ecf-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=7c3e…", "mimeType": "image/webp", "size": 236114, "purpose": "TICKET_ATTACHMENT" }
      ],
      "createdAt": "2026-11-03T11:30:00Z"
    }
  ],
  "afterSales": {
    "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131", "deliveredAt": "2026-10-08T10:20:00Z" },
    "items": [
      {
        "id": "01a1a12c-56c0-7b11-8c22-4d5e6f708192",
        "orderItemId": "01a1065b-2900-7a11-9b22-3c4d5e6f7a81",
        "product": { "id": "01a0901f-d380-7a88-9746-4ec8936c3d68", "name": "BOROFONE BW06 Écouteurs TWS", "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": 3,
        "issue": "DEFECTIVE"
      }
    ],
    "claimableUntil": "2026-11-07T10:20:00Z",
    "resolution": null
  },
  "allowedActions": ["REPLY", "CLOSE"]
}

Errors

StatusCodeWhen
400VALIDATION_FAILEDField codes: REQUIRED (orderId for the types that need it, items for AFTER_SALES), PHOTO_REQUIRED (claim without photo), TOO_MANY (more than 5 attachments), ITEMS_NOT_ALLOWED (items on another type), UNKNOWN_ORDER_ITEM, DUPLICATE_ITEM, FILE_NOT_FOUND, FILE_ALREADY_ATTACHED, lengths.
404NOT_FOUNDorderId is not one of my orders.
422FILE_NOT_READYAn attachment upload was not completed.
422FILE_PURPOSE_MISMATCHAn attachment was not uploaded with purpose TICKET_ATTACHMENT.
409ORDER_NOT_DELIVEREDAFTER_SALES on an order that is not DELIVERED.
422AFTER_SALES_WINDOW_EXPIREDPast the after-sales window (details.claimableUntil).
422QUANTITY_NOT_ALLOWEDAn item's qty above what is left to claim (details: { orderItemId, claimableQty }).
429RATE_LIMITEDMore than 10 tickets in 24 hours for the reseller (assumption, anti-spam).

Rules & side effects

  1. Ticket (+ TicketItem rows for a claim, + the first TicketMessage), number TKT-YYYY-NNNNNN, status OPEN; attachments linked to the message.
  2. Claims are flagged « SAV » in the support queue (BO-55, BO-69); the claimed quantities count in the order line's claimedQty, so the same units cannot be claimed or returned twice.
  3. Outbox ticket.created → support queue in-app (and SSE for AFTER_SALES), acknowledgement in the reseller's inbox; CRM timeline entry CLAIM for claims.
  4. « Contacter le support » from an order pre-fills orderId and type: ORDER_ISSUE.

Used by — RA-33 (« Nouvelle demande »), « Contacter le support » and « Signaler un problème » on RA-22, RW-14.

GET /tickets/{ticketId}

tickets.get · Access session — reseller (self) · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements SUP-01, SUP-04

Purpose. « Conversation »: the ticket with its public messages and, for a claim, the claimed items and the resolution.

Request — path ticketId.

Response — 200 OK: a TicketDetail. Example: the claim above two days later — HOCO asked a question, the reseller answered, a replacement was decided.

{
  "id": "01a1a12c-56c0-7989-951b-4022807290b6",
  "number": "TKT-2026-000046",
  "type": "AFTER_SALES",
  "subject": "BOROFONE BW06 — boîtier de charge défectueux",
  "status": "RESOLVED",
  "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131" },
  "afterSalesResolution": "REPLACEMENT",
  "lastMessageAt": "2026-11-05T09:40:00Z",
  "lastMessagePreview": "Réclamation résolue : remplacement. La commande CMD-2026-000163 a été créée pour vous.",
  "unreadCount": 0,
  "rating": null,
  "createdAt": "2026-11-03T11:30:00Z",
  "resolvedAt": "2026-11-05T09:40:00Z",
  "closedAt": null,
  "messages": [
    {
      "id": "01a1a12c-56c0-7090-99d0-d51e9b32f39a",
      "author": { "type": "RESELLER", "displayName": "Mohamed Benali" },
      "body": "Trois paires BW06 de la commande CMD-2026-000131 : le boîtier ne charge plus les écouteurs après une semaine. Clients revenus au magasin.",
      "attachments": [
        { "id": "01a1a12a-8200-79a8-93c8-c5b89aac7ecf", "url": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a12a-8200-79a8-93c8-c5b89aac7ecf.webp?X-Amz-Expires=300&X-Amz-Signature=5d0b…", "thumbnailUrl": "https://storage.hoco.dz/hoco-private/ticket_attachment/2026/11/01a1a12a-8200-79a8-93c8-c5b89aac7ecf-thumb.webp?X-Amz-Expires=300&X-Amz-Signature=8a41…", "mimeType": "image/webp", "size": 236114, "purpose": "TICKET_ATTACHMENT" }
      ],
      "createdAt": "2026-11-03T11:30:00Z"
    },
    {
      "id": "01a1a150-f5c0-7f42-97e3-64c86987a26c",
      "author": { "type": "SUPPORT", "displayName": "Sarah — Service client" },
      "body": "Merci. Les voyants du boîtier s'allument-ils quand il est branché ? Pouvez-vous nous envoyer une vidéo courte ou une photo du boîtier branché ?",
      "attachments": [],
      "createdAt": "2026-11-03T12:10:00Z"
    },
    {
      "id": "01a1a2b0-4a00-7c13-8d24-5e6f708192a3",
      "author": { "type": "RESELLER", "displayName": "Mohamed Benali" },
      "body": "Aucun voyant ne s'allume, même avec un autre câble.",
      "attachments": [],
      "createdAt": "2026-11-04T08:05:00Z"
    },
    {
      "id": "01a1aa0c-1e00-7d24-9e35-6f708192a3b4",
      "author": { "type": "SYSTEM", "displayName": "HOCO" },
      "body": "Réclamation résolue : remplacement. La commande CMD-2026-000163 a été créée pour vous.",
      "attachments": [],
      "createdAt": "2026-11-05T09:40:00Z"
    }
  ],
  "afterSales": {
    "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131", "deliveredAt": "2026-10-08T10:20:00Z" },
    "items": [
      {
        "id": "01a1a12c-56c0-7b11-8c22-4d5e6f708192",
        "orderItemId": "01a1065b-2900-7a11-9b22-3c4d5e6f7a81",
        "product": { "id": "01a0901f-d380-7a88-9746-4ec8936c3d68", "name": "BOROFONE BW06 Écouteurs TWS", "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": 3,
        "issue": "DEFECTIVE"
      }
    ],
    "claimableUntil": "2026-11-07T10:20:00Z",
    "resolution": {
      "type": "REPLACEMENT",
      "note": "Défaut de batterie du boîtier confirmé. Trois paires neuves vous sont envoyées gratuitement ; votre délégué récupère les paires défectueuses.",
      "decidedAt": "2026-11-05T09:40:00Z",
      "replacementOrder": { "id": "01a1aa0c-1e00-7a35-8b46-708192a3b4c5", "number": "CMD-2026-000163", "status": "SUBMITTED" },
      "return": null,
      "creditNote": null,
      "repair": null,
      "rejectionReason": null
    }
  },
  "allowedActions": ["REPLY", "CLOSE", "RATE"]
}
FieldNotes
messages[].authorSupport agents appear by first name and team; SYSTEM messages record status changes and resolutions. Internal notes are never returned.
afterSales.resolutionREPLACEMENT: free order (price 0) that waits for the back-office confirmation like any order — link to RA-22. RETURN: a return request already APPROVED with its instructions — link to RA-40. CREDIT_NOTE: credit note on the order's invoice. REPAIR: expected date. REJECTED: status REJECTED and rejectionReason (out of warranty, misuse…); the reseller may contest once by replying.
allowedActionsREPLY unless CLOSED; CLOSE from OPEN, IN_PROGRESS, RESOLVED, REJECTED; RATE when closing a resolved or rejected ticket.

Errors

StatusCodeWhen
404NOT_FOUNDNot my ticket.

Rules & side effects — reading the conversation marks the support messages as read (unreadCount → 0) — the one documented side effect of this read. Attachment URLs are signed for 5 minutes. Live: ticket.replied appends messages while the screen is open.

Used by — RA-34 (« Conversation »), RW-14, deep links of ticket notifications.

POST /tickets/{ticketId}/messages

tickets.reply · Access session — reseller (self) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements SUP-01

Purpose. Adds my reply to the conversation (text and up to 5 attachments).

Request

interface ReplyRequest {
  message: string;                    // 1–2 000 characters
  attachmentFileIds?: Uuid[];         // 0–5, TICKET_ATTACHMENT
}
{ "message": "Non, le magasin était fermé hier après-midi. Le livreur peut passer demain matin entre 9h et 12h.", "attachmentFileIds": [] }

Response — 201 Created: the new message and the ticket after the change.

{
  "message": {
    "id": "01a1a2c6-9b00-7e35-8f46-708192a3b4c5",
    "author": { "type": "RESELLER", "displayName": "Mohamed Benali" },
    "body": "Non, le magasin était fermé hier après-midi. Le livreur peut passer demain matin entre 9h et 12h.",
    "attachments": [],
    "createdAt": "2026-11-03T09:20:00Z"
  },
  "ticket": {
    "id": "01a1965f-7240-7a21-a8d4-11ef017fd953",
    "number": "TKT-2026-000045",
    "type": "DELIVERY_ISSUE",
    "subject": "Commande CMD-2026-000155 pas encore reçue",
    "status": "IN_PROGRESS",
    "order": { "id": "01a187cf-6b40-7c6f-894d-b1f256947da9", "number": "CMD-2026-000155" },
    "afterSalesResolution": null,
    "lastMessageAt": "2026-11-03T09:20:00Z",
    "lastMessagePreview": "Non, le magasin était fermé hier après-midi. Le livreur peut passer demain matin entre 9h et 12h.",
    "unreadCount": 0,
    "rating": null,
    "createdAt": "2026-11-01T09:10:00Z",
    "resolvedAt": null,
    "closedAt": null
  }
}

Errors

StatusCodeWhen
400VALIDATION_FAILEDEmpty or too long message; attachment field codes as for tickets.create.
404NOT_FOUNDNot my ticket.
422FILE_NOT_READYAttachment upload not completed.
422FILE_PURPOSE_MISMATCHAttachment not uploaded with purpose TICKET_ATTACHMENT.
409TICKET_TRANSITION_NOT_ALLOWEDThe ticket is CLOSED — open a new ticket referencing it (details.from: "CLOSED"). Contesting a REJECTED claim a second time gives the same answer.

Rules & side effects — a reply moves WAITING_RESELLER, RESOLVED or REJECTED (first contest) back to IN_PROGRESS and notifies the assignee in-app; on OPEN and IN_PROGRESS the status is unchanged. The auto-close timer of resolved tickets restarts.

Used by — reply field of RA-34 and RW-14.

POST /tickets/{ticketId}/close

tickets.close · Access session — reseller (self) · Apps RA, RW · Idempotency-Key optional · Release V1 · Requirements SUP-01

Purpose. « Clôturer »: the reseller closes the ticket, optionally rating the answer from 1 to 5.

Request

interface CloseTicketRequest {
  rating?: { score: 1 | 2 | 3 | 4 | 5; comment?: string };   // comment ≤ 300
}
{ "rating": { "score": 5, "comment": "Remplacement rapide, merci." } }

Response — 200 OK: the TicketSummary, status CLOSED.

{
  "id": "01a1a12c-56c0-7989-951b-4022807290b6",
  "number": "TKT-2026-000046",
  "type": "AFTER_SALES",
  "subject": "BOROFONE BW06 — boîtier de charge défectueux",
  "status": "CLOSED",
  "order": { "id": "01a1065b-2900-748c-88c5-976d443033f4", "number": "CMD-2026-000131" },
  "afterSalesResolution": "REPLACEMENT",
  "lastMessageAt": "2026-11-05T09:40:00Z",
  "lastMessagePreview": "Réclamation résolue : remplacement. La commande CMD-2026-000163 a été créée pour vous.",
  "unreadCount": 0,
  "rating": { "score": 5, "comment": "Remplacement rapide, merci.", "ratedAt": "2026-11-05T12:15:00Z" },
  "createdAt": "2026-11-03T11:30:00Z",
  "resolvedAt": "2026-11-05T09:40:00Z",
  "closedAt": "2026-11-05T12:15:00Z"
}

Errors

StatusCodeWhen
400VALIDATION_FAILEDscore outside 1–5, comment too long.
404NOT_FOUNDNot my ticket.
409TICKET_TRANSITION_NOT_ALLOWEDAlready CLOSED, or WAITING_RESELLER (support is waiting for my answer: reply instead, or let the auto-close run) — details: { from, to: "CLOSED", allowed }.

Rules & side effects — closedAt, rating stored on the ticket (support KPI on BO-55); closing does not cancel a replacement order or a return already created by a resolution. Resolved and rejected tickets are closed automatically after support.autoCloseDays (default 7) without reply.

Used by — RA-34, RW-14.

GET /support/contacts

support.contacts · Access session — any signed-in reseller, PENDING_VALIDATION included · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements SUP-03

Purpose. The support phone, e-mail and hours, and the FAQ shown at the top of « Assistance ». The WhatsApp button uses support.whatsapp.

Request — no parameters (Accept-Language selects the FAQ language).

Response — 200 OK, ETag, Cache-Control: private, max-age=3600.

{
  "phone": "+213412345678",
  "email": "support@hoco.dz",
  "hours": [
    { "days": ["SUN", "MON", "TUE", "WED", "THU"], "opens": "08:30", "closes": "17:00" }
  ],
  "timezone": "Africa/Algiers",
  "isOpenNow": true,
  "outsideHoursMessage": "Notre équipe vous répond du dimanche au jeudi, de 8h30 à 17h. Laissez-nous un message, nous vous rappelons.",
  "faq": [
    { "id": "01a0f153-dc00-745c-9922-1f433ab10dd2", "category": "Commandes", "q": "Quel est le montant minimum de commande ?", "a": "10 000,00 DA TTC de produits, hors frais de livraison." },
    { "id": "01a0f153-dfe8-76ad-9d82-21d5c46a3ba9", "category": "Commandes", "q": "Pourquoi ma commande est-elle « En attente » ?", "a": "Chaque commande est vérifiée et confirmée par l'équipe HOCO avant sa préparation. Vous êtes notifié dès qu'elle est validée." },
    { "id": "01a0f153-e3d0-7614-9bf6-96b27f009aa5", "category": "Paiements", "q": "Comment déclarer un virement, un versement CCP ou un paiement BaridiMob ?", "a": "Compte › Paiements › Déclarer un paiement : indiquez le montant, la date et la référence, et joignez la photo du reçu." },
    { "id": "01a0f153-e7b8-77da-bc3b-124e41f4a169", "category": "Compte", "q": "Comment changer le nom du magasin ou le numéro principal ?", "a": "Ouvrez une demande d'assistance : ces informations sont modifiées par HOCO après vérification." }
  ]
}

Errors — none specific.

Rules & side effects — contacts and hours come from the settings (support.phone, support.email, support.hours — the same values appConfig.get gives before sign-in); FAQ entries are managed in the back-office settings. isOpenNow is computed in Africa/Algiers and ignores public holidays (assumption — a holiday calendar can be added to the settings).

Used by — RA-32, RW-14, the contact block of RA-06.

GET /support/whatsapp

support.whatsapp · Access optional — guest or signed-in user · Apps RA, RW · Idempotency-Key — · Release V1 · Requirements SUP-05, ADM-08

Purpose. Resolves the WhatsApp number of the caller's support region for the fixed WhatsApp button, with a ready wa.me link, the hours and how the region was found.

Request

QueryTypeRequiredNotes
lat, lngnumberno (together)Device position (guests); WGS84
wilayaCodestringnoWilaya picked by a guest when the location is unavailable
productIdUuidnoPre-fills the message with the product (from a product page)
orderIdUuidnoSigned-in only: pre-fills the message with my order number

Signed-in user (no parameter needed):

GET /v1/support/whatsapp

Response — 200 OK, Cache-Control: private, max-age=300.

interface SupportWhatsapp {
  region: { id: Uuid; name: string } | null;      // null with the DEFAULT number
  wilaya: { code: string; name: string } | null;  // the wilaya used for the resolution
  whatsappNumber: string;                          // E.164
  waLink: string;                                  // https://wa.me/<digits>?text=<url-encoded>
  prefillText: string;
  hours: { days: ("SUN" | "MON" | "TUE" | "WED" | "THU" | "FRI" | "SAT")[]; opens: string; closes: string }[];
  timezone: "Africa/Algiers";
  isOpenNow: boolean;
  outsideHoursMessage: string | null;
  resolvedFrom: "ACCOUNT" | "LOCATION" | "WILAYA" | "DEFAULT";
}
{
  "region": { "id": "01a0c7e2-41d0-7a6b-9c3e-5f1a2b3c4d5e", "name": "Ouest" },
  "wilaya": { "code": "31", "name": "Oran" },
  "whatsappNumber": "+213770900310",
  "waLink": "https://wa.me/213770900310?text=Bonjour%2C%20je%20suis%20Magasin%20El%20Wafa%20(REV-000123).%20",
  "prefillText": "Bonjour, je suis Magasin El Wafa (REV-000123). ",
  "hours": [ { "days": ["SUN", "MON", "TUE", "WED", "THU"], "opens": "08:30", "closes": "17:00" } ],
  "timezone": "Africa/Algiers",
  "isOpenNow": true,
  "outsideHoursMessage": null,
  "resolvedFrom": "ACCOUNT"
}

A guest on a product page, location allowed, standing in Sidi Bel Abbès (wilaya 22, region Ouest):

GET /v1/support/whatsapp?lat=35.1899&lng=-0.6309&productId=01a08ac2-8900-7f6d-937c-c81b37e89a5b
{
  "region": { "id": "01a0c7e2-41d0-7a6b-9c3e-5f1a2b3c4d5e", "name": "Ouest" },
  "wilaya": { "code": "22", "name": "Sidi Bel Abbès" },
  "whatsappNumber": "+213770900310",
  "waLink": "https://wa.me/213770900310?text=Bonjour%2C%20je%20souhaite%20des%20informations%20sur%20HOCO%20N32%20Chargeur%20rapide%2030W%20(r%C3%A9f.%20N32).",
  "prefillText": "Bonjour, je souhaite des informations sur HOCO N32 Chargeur rapide 30W (réf. N32).",
  "hours": [ { "days": ["SUN", "MON", "TUE", "WED", "THU"], "opens": "08:30", "closes": "17:00" } ],
  "timezone": "Africa/Algiers",
  "isOpenNow": true,
  "outsideHoursMessage": null,
  "resolvedFrom": "LOCATION"
}
FieldNotes
resolvedFromACCOUNT: signed in — the region of the wilaya declared in the account (Reseller.wilayaCode → SupportRegionWilaya), query parameters ignored. LOCATION: nearest commune centroid to lat/lng → its wilaya → region. WILAYA: the wilayaCode picked by a guest. DEFAULT: nothing usable, or no region covers the wilaya — the national number support.whatsapp from the settings, region: null.
waLinkOpened as is (url_launcher on mobile, a new tab on the web); the digits are the E.164 number without +. No message is sent by the platform (WhatsApp Business API is V2).
prefillTextSigned-in: store name and REV- number (plus the order number with orderId); guest: a neutral greeting, the product with productId. Never a price.
outsideHoursMessageSet when isOpenNow is false, shown under the button (« Réponse dès 8h30 »).

Errors

StatusCodeWhen
400VALIDATION_FAILEDlat without lng (or the reverse), coordinates out of range, unknown wilayaCode, orderId sent by a guest.
404NOT_FOUNDproductId not visible to the caller, orderId not mine.

Rules & side effects

  1. Resolution order: account → location → wilaya → default (D-36 decides the regions; with one national region every caller gets the same number and the feature works unchanged).
  2. Coordinates are used for the lookup only: never stored, logged rounded to 0.1°. A position outside Algeria resolves to DEFAULT.
  3. Signed-in apps can open the button straight from Me.supportRegion (me) and call this operation only to refresh hours or build a contextual message; guests always call it (the app asks for the location permission only when the button is tapped, and falls back to a wilaya picker).
  4. Public rate limit: 30 calls per minute per IP for guests.

Used by — the fixed WhatsApp button of every reseller app screen and of the reseller web layout (guest browsing RA-41 included), RA-32, RW-14; empty search results of the catalog.

On this page