Me, devices, app config & reference data
The signed-in user's profile and devices, the start-up configuration of each app (version policy, maintenance, public settings) and the cacheable reference data: wilayas, communes and reference lists.
This group holds what every client loads before its first business screen:
me.get/me.update— the signed-in user as theMeobject (defined once on the authentication page): identity, account status, roles, permissions, customer type, support region, reseller or delegate context.me.devices.*— the push registration of each mobile install (Firebase Cloud Messaging token, app version, locale).appConfig.get— public, called on the splash screen before sign-in: version policy, maintenance flag and the public business settings the apps need to render rules (minimum order, GPS tolerance, support contacts…). Guests use it too.reference.*— wilayas and communes (public,ETagcached) and the reference lists behind every dropdown (session required).
| Method | Path | Operation | Access | Apps | Release |
|---|---|---|---|---|---|
| GET | /me | me.getMy profile, roles, permissions, scope (reseller or delegate context) and account status | session | RA · RW · DA · BO | V1 |
| PATCH | /me | me.updateUpdate my name, locale or avatar | session | RA · RW · DA · BO | V1 |
| POST | /me/deletion-request | me.deleteAccountRequest deletion of my account (Play policy): sessions revoked, personal data anonymised after the legal retention period; business records kept | sessionIdempotency-Key | RA · RW | V1 |
| POST | /me/devices | me.devices.registerRegister or refresh this device's FCM token and app version | sessionIdempotency-Key | RA · DA | V1 |
| DELETE | /me/devices/{deviceId} | me.devices.removeUnregister a device | session | RA · DA | V1 |
| GET | /app-config | appConfig.getVersion policy (min/latest/forced), maintenance flag and public business settings for an app/platform | public | RA · RW · DA | V1 |
| GET | /reference/wilayas | reference.wilayasWilayas (code, FR/AR names); cacheable with ETag | public | RA · RW · DA · BO | V1 |
| GET | /reference/wilayas/{wilayaCode}/communes | reference.communesCommunes of a wilaya; cacheable with ETag | public | RA · RW · DA · BO | V1 |
| GET | /reference/lists | reference.listsReference lists (business types, potentials, visit types, not-done reasons, issue tags, ticket types, payment methods); cacheable | session | RA · RW · DA · BO | V1 |
Besides the errors listed per operation, every operation can answer 426 UPGRADE_REQUIRED (except appConfig.get, auth.logout and, for 72 hours after the minimum was raised, the delegate app's sync.batch — AUTH-09), 429 RATE_LIMITED, 500 INTERNAL_ERROR and 503 MAINTENANCE; session operations also 401 UNAUTHENTICATED and 403 ACCOUNT_SUSPENDED. See the error registry.
Start-up sequence
Splash (RA-01, DA-01). GET /app-config?app=RESELLER_APP&platform=android&version=1.4.2. If forceUpdate → forced-update screen (RA-36 / DA-28) with storeUrl; if maintenance.enabled → maintenance screen with message and until; if updateAvailable → dismissible banner.
Session check. With a stored token: GET /me. 401 → clear the token; the reseller app continues as a guest (RA-02 / RA-41), the delegate app goes to sign-in (DA-02). 403 PASSWORD_CHANGE_REQUIRED or user.mustChangePassword → change-password screen.
Device. Signed in on mobile: POST /me/devices with the current FCM token (every start, cheap and idempotent).
Reference data. Revalidate the cached wilayas, communes and lists with If-None-Match (usually 304). The delegate app also stores the communes of its zone's wilayas for offline prospect creation.
Operations
GET /me
me.get · Access session (any user type) · Apps RA, RW, DA, BO · Idempotency-Key — · Release V1 · Requirements ACL-02, ACL-03, AUTH-08
Purpose. Returns the signed-in user with everything the client needs to route and gate its screens: status, roles, permissions, customer type, support region and the reseller or delegate context.
Request — no parameters.
Response — 200 OK, Cache-Control: private, no-store. Body: a Me object (examples for the three user types on the authentication page).
{
"id": "01a19c3e-5a10-7b21-8f4e-2c6d9a1b3e57",
"type": "RESELLER",
"displayName": "Mohamed Benali",
"phone": "+213555123456",
"phoneVerified": true,
"email": null,
"locale": "fr",
"status": "ACTIVE",
"roles": ["RESELLER"],
"permissions": [],
"mustChangePassword": false,
"avatar": null,
"consents": {
"termsVersion": "2026-11",
"termsAcceptedAt": "2026-11-03T08:12:00Z",
"locationTrackingVersion": null,
"locationTrackingAcceptedAt": null
},
"supportRegion": { "id": "01a0c7e2-41d0-7a6b-9c3e-5f1a2b3c4d5e", "name": "Ouest", "whatsappNumber": "+213770900310" },
"reseller": {
"id": "01a17f02-93c4-7e8a-b1d2-4f5a6b7c8d90",
"number": "REV-000123",
"storeName": "Magasin El Wafa",
"customerType": "DEMI_GROS",
"status": "FIRST_ORDER",
"validationStatus": "APPROVED",
"rejectionReason": null,
"canResubmit": false,
"wilayaCode": "31",
"assignedDelegate": { "id": "01a0f2b1-7d3c-7a45-9e61-0b2c3d4e5f60", "displayName": "Yacine Belkacem", "phone": "+213770123456" }
},
"createdAt": "2026-11-03T08:12:00Z",
"lastLoginAt": "2026-11-05T07:45:10Z"
}Errors — none specific (401 UNAUTHENTICATED with details.reason when the session is gone).
Rules & side effects
- Read-only, computed on every call (never cached by the server across users). Mapping:
displayName←User.name;emailisnullfor the placeholder addresses of phone-only users;avatar←User.avatarFileId;consents←User.termsVersion/termsAcceptedAtandDelegateProfile.locationConsentVersion/locationConsentAt; the reseller context ←Reseller;supportRegion←SupportRegionWilayaof the reseller's wilaya;delegate←DelegateProfile(itsidequalsUser.id) andDelegateZone. - Clients refresh
Meat start-up, when the app returns to the foreground after more than 15 minutes, and when they receive a notification whoseeventKeystarts withaccount.(validation, rejection, customer-type change, suspension).
Used by — splash of RA-01 and DA-01; account hubs RA-27 and DA-24; RA-06 (pending / rejected state); the back-office shell BO-03 (navigation by permission); RW-12.
PATCH /me
me.update · Access session (any user type) · Apps RA, RW, DA, BO · Idempotency-Key optional · Release V1 · Requirements NFR-06
Purpose. Lets users change their own name, language and avatar, change their phone after an OTP check, and record consents (terms, delegate location tracking).
Request
interface MeUpdateRequest {
displayName?: string; // 2–80 characters
locale?: "fr" | "ar" | "en";
avatarFileId?: Uuid | null; // a completed File of purpose AVATAR uploaded by the caller; null removes it
phone?: string; // new phone — requires phoneVerificationToken
phoneVerificationToken?: string; // auth.otp.verify, purpose PHONE_CHANGE, same phone
consents?: {
terms?: { version: string }; // resellers: re-acceptance of updated terms
locationTracking?: { accepted: boolean; version: string }; // delegates (DA-04)
};
}{
"locale": "ar",
"avatarFileId": "01a1a0b9-4c2d-7e6f-8a1b-2c3d4e5f6a7b"
}Response — 200 OK: the updated Me.
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Field errors (TOO_SHORT, INVALID_ENUM, INVALID_PHONE); phone without phoneVerificationToken; consents.locationTracking sent by a non-delegate or consents.terms by a non-reseller. |
| 404 | NOT_FOUND | avatarFileId unknown or uploaded by someone else. |
| 422 | FILE_NOT_READY | The avatar file was never completed (files.completeUpload). |
| 409 | PHONE_ALREADY_EXISTS | The new phone belongs to another account or another point of sale. |
| 422 | FILE_PURPOSE_MISMATCH | The file's purpose is not AVATAR. |
| 422 | VERIFICATION_TOKEN_INVALID | Token expired, used, of another purpose, issued for another phone or to another user. |
| 422 | TERMS_NOT_ACCEPTED | consents.terms.version is not the current legal.termsVersion of appConfig.get. |
Rules & side effects
- Name.
displayNameis stored asUser.name. For a reseller it is the store manager's name: it is written toUser.nameand the reseller's owner name in one transaction (the same mirroring applies when the owner name changes through the reseller profile). - Locale. Drives the language of push, SMS and email templates and the default of
Accept-Language-less calls. V1 ships French;arandenare accepted from day one (NFR-06) and offered once their translations ship (D-09). - Phone change.
User.phonechanges; for a reseller the main phone of theResellerchanges too (uniqueness re-checked, previous value kept in the history). Other sessions stay valid.security.phone_changedis sent by SMS to the old number and in-app (Assumption — event to add to the notifications matrix). - Consents. Terms go to
User.termsVersion/termsAcceptedAt; location consent toDelegateProfile.locationConsentVersion/locationConsentAt. A delegate withdrawing location consent (accepted: false) clears them: the app stops sending pings and a tour cannot start until consent is given again (SEC-07). - Not editable here: email (staff: by an administrator; resellers: store profile), customer type (back-office only — AUTH-11), roles and status.
- Audit
UPDATEwith the changed fields (passwordand tokens are never logged).
Used by — settings RA-35 (language, phone), DA-26, RW-12, BO-68; avatar on RA-27 and DA-24; location consent on DA-04 (« J'accepte »).
POST /me/devices
me.devices.register · Access session (RESELLER, DELEGATE) · Apps RA, DA · Idempotency-Key required · Release V1 · Requirements AUTH-09, NOT-02
Purpose. Registers this app install for push notifications, or refreshes its FCM token, app version and locale.
Request
interface DeviceRegistration {
deviceId: string; // install id — must equal the X-Device-Id header
platform: "android" | "ios";
pushToken: string | null; // FCM registration token (Device.fcmToken); null when notification permission is denied
appId: "RESELLER_APP" | "DELEGATE_APP"; // ClientApp
appVersion: string; // semver, "1.4.2"
locale: "fr" | "ar" | "en"; // device language, informative (Me.locale drives templates)
osVersion?: string; // "Android 14"
model?: string; // "Samsung SM-A155F"
}{
"deviceId": "5f8c2a1e-7b3d-4e9f-a6c0-1d2e3f4a5b6c",
"platform": "android",
"pushToken": "fJ3k9L0aQ2W:APA91bH7xY4Zr2mTq8VwN1cPs5dE6gK0uB3oR9tL7yX2nM4",
"appId": "RESELLER_APP",
"appVersion": "1.4.2",
"locale": "fr",
"osVersion": "Android 14",
"model": "Samsung SM-A155F"
}Response — 201 Created on the first registration of this install, 200 OK on a refresh.
{
"deviceId": "5f8c2a1e-7b3d-4e9f-a6c0-1d2e3f4a5b6c",
"platform": "android",
"appId": "RESELLER_APP",
"appVersion": "1.4.2",
"pushEnabled": true,
"registeredAt": "2026-11-03T08:12:04Z",
"lastSeenAt": "2026-11-05T07:45:12Z"
}Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | deviceId differs from X-Device-Id (field code DEVICE_ID_MISMATCH), bad semver, unknown platform. |
| 403 | WRONG_APP | appId does not match the user type (a reseller registering DELEGATE_APP). |
Rules & side effects
- Upsert on (
userId,deviceId). A device belongs to one user at a time: if the same install id is bound to another user (shared phone, previous user signed out offline), that binding is removed and the previous user stops receiving push on it.platformis stored asANDROID/IOS;pushEnabledis true when a token is present. - The device is bound to the current session: revoking the session (sign-out, password change,
auth.sessions.revoke, suspension) clears its push token. - An FCM token already registered on another device id is removed there (tokens are unique).
- Call it after every sign-in, at each app start, on FCM
onTokenRefresh, after an app update and when the user changes the notification permission.pushToken: nullkeeps the device known for version statistics (BO-66) without push. - FCM answers
UNREGISTEREDfor a dead token → the notification worker deletes it; the app re-registers on its next start. Devices not seen for 90 days are pruned.
Used by — after sign-in on RA-07, RA-05 and DA-02; app start; the « Notifications sur cet appareil » switch of RA-35 and DA-26.
DELETE /me/devices/{deviceId}
me.devices.remove · Access session (RESELLER, DELEGATE; own devices) · Apps RA, DA · Idempotency-Key optional · Release V1 · Requirements AUTH-09
Purpose. Stops push notifications to one install without signing it out.
Request
| Path parameter | Type | Notes |
|---|---|---|
deviceId | string | The install id (X-Device-Id), not a server id. |
Response — 204 No Content, also when the device is already absent (idempotent).
Errors — none specific.
Rules & side effects — the Device row is deleted, so no push reaches this install; the session on that device stays valid (use auth.sessions.revoke to sign it out). The in-app inbox keeps receiving every notification.
Used by — the « Notifications sur cet appareil » switch turned off in RA-35 and DA-26.
GET /app-config
appConfig.get · Access public (guests included) · Apps RA, RW, DA · Idempotency-Key — · Release V1 · Requirements ADM-07, ADM-03
Purpose. Gives an app, before anything else, its version policy (minimum, latest, forced update), the maintenance state and the public business settings it needs to display rules and contacts.
Request
| Query parameter | Required | Values | Notes |
|---|---|---|---|
app | yes | RESELLER_APP, DELEGATE_APP, RESELLER_WEB | ClientApp value. |
platform | yes | android, ios, web | |
version | no | semver (1.4.2) | When given, the server computes forceUpdate and updateAvailable. |
Response — 200 OK, Cache-Control: public, max-age=300, ETag; 304 Not Modified on a matching If-None-Match.
interface AppConfig {
app: "RESELLER_APP" | "DELEGATE_APP" | "RESELLER_WEB";
platform: "android" | "ios" | "web";
minVersion: string; // AppVersionPolicy.minVersion
latestVersion: string;
forceUpdate: boolean; // version < minVersion
updateAvailable: boolean; // minVersion <= version < latestVersion
storeUrl: string | null; // null for the web
maintenance: {
enabled: boolean;
message: string | null; // AppVersionPolicy message in the request locale
until: Iso | null;
};
settings: PublicSettings; // public subset of the settings catalogue, see the table
signup: { // reseller apps only — sign-up happens before any session
customerTypes: ("SUPER_GROS" | "DEMI_GROS" | "DETAIL")[];
businessTypes: { code: string; label: string; sortOrder: number }[]; // BUSINESS_TYPE items
} | null;
legal: { termsUrl: string; privacyUrl: string; termsVersion: string } | null; // reseller apps only
serverTime: Iso; // lets the app detect a wrong device clock
}{
"app": "RESELLER_APP",
"platform": "android",
"minVersion": "1.3.0",
"latestVersion": "1.5.0",
"forceUpdate": false,
"updateAvailable": true,
"storeUrl": "https://play.google.com/store/apps/details?id=dz.hoco.reseller",
"maintenance": { "enabled": false, "message": null, "until": null },
"settings": {
"currency": "DZD",
"pricing": { "displayTaxMode": "TTC" },
"orders": {
"minAmount": 1000000,
"minQtyPerLine": 1,
"maxQtyPerLine": null,
"cancelAllowedStatuses": ["SUBMITTED", "PENDING"],
"assistedConfirmationHours": 72
},
"returns": { "windowDays": 7 },
"afterSales": { "windowDays": 30 },
"catalog": { "pricesVisibleBeforeValidation": true },
"payments": { "enabledMethods": ["CASH_ON_DELIVERY", "BANK_TRANSFER", "CCP", "BARIDIMOB"] },
"delivery": { "feeMode": "FREE", "freeThreshold": null },
"support": { "defaultWhatsappNumber": "+213770900300" }
},
"signup": {
"customerTypes": ["SUPER_GROS", "DEMI_GROS", "DETAIL"],
"businessTypes": [
{ "code": "PHONE_SHOP", "label": "Boutique de téléphonie", "sortOrder": 10 },
{ "code": "ACCESSORIES_SHOP", "label": "Accessoires mobiles", "sortOrder": 20 },
{ "code": "MULTISERVICE", "label": "Multiservices / taxiphone", "sortOrder": 30 },
{ "code": "ELECTRONICS", "label": "Électronique / électroménager", "sortOrder": 40 },
{ "code": "WHOLESALER", "label": "Grossiste", "sortOrder": 50 },
{ "code": "OTHER", "label": "Autre", "sortOrder": 90 }
]
},
"legal": {
"termsUrl": "https://app.hoco.dz/conditions",
"privacyUrl": "https://app.hoco.dz/confidentialite",
"termsVersion": "2026-11"
},
"serverTime": "2026-11-05T07:45:02Z"
}Public settings (settings) — the settings of the settings catalogue marked as needed by the apps, nested by their dotted key (orders.minAmount → settings.orders.minAmount). The catalogue is the reference for types, defaults and allowed values; changes are made on BO-64.
| Key | Default | Sent to | Use in the app |
|---|---|---|---|
currency | DZD | all | Formatting (1 250,00 DA). Constant. |
pricing.displayTaxMode | TTC | RA, RW, DA | Which value of Price is shown large (D-17). |
orders.minAmount | 0 (none) — the example shows a configured 10 000,00 DA | RA, RW, DA | Cart progress bar « Plus que 1 545,00 DA pour commander »; enforced by cart.checkout (D-19). |
orders.minQtyPerLine, orders.maxQtyPerLine | 1, null | RA, RW, DA | Quantity stepper bounds (variants may override). |
orders.cancelAllowedStatuses | SUBMITTED, PENDING | RA, RW | Shows or hides « Annuler la commande ». |
orders.assistedConfirmationHours | 72 | RA, RW, DA | Countdown on assisted orders (RA-23, DA-17) — D-23. |
returns.windowDays | 7 | RA, RW | Days after delivery during which « Demander un retour » is offered (422 RETURN_WINDOW_EXPIRED after) — D-35. |
afterSales.windowDays | 30 | RA, RW | Days after delivery for an after-sales claim (422 AFTER_SALES_WINDOW_EXPIRED after) — D-35 may replace it by the product warranty. |
catalog.pricesVisibleBeforeValidation | true | RA, RW | Whether a PENDING_VALIDATION reseller sees the prices of their declared type (D-18); guests never see prices. |
payments.enabledMethods | all five | RA, RW, DA | Payment choices at checkout and in payment declarations (D-03). |
delivery.feeMode, delivery.freeThreshold | FREE, null | RA, RW, DA | « Livraison offerte dès … » hint; the cart computes the actual fee (D-20). |
support.defaultWhatsappNumber | to provide by HOCO | RA, RW | Last fallback of the WhatsApp button when neither Me.supportRegion nor support.whatsapp gives a number. |
field.gpsToleranceMeters | 150 | DA | Check-in radius drawn on the map (DA-08, DA-10) — D-06. |
field.gpsMaxAccuracyMeters | 100 | DA | A fix less precise than this is not used for check-in; the app waits for a better one. |
field.allowOutOfToleranceCheckIn | false | DA | Whether DA-28 offers a justified check-in outside the radius. |
field.locationIntervalSeconds, field.locationDistanceMeters | 300, 200 | DA | Location sampling while a tour is IN_PROGRESS — D-07. |
field.locationRetentionDays | 90 | DA | Shown on the consent screen DA-04 (SEC-07, D-11). |
field.reportRequiredToEndVisit, field.closingRequiresSync | true, true | DA | Visit end and tour closing guards (DA-12, DA-20). |
invitations.validityDays | 30 | DA | « Lien valable 30 jours » on DA-16. |
field.* and invitations.* are only sent to DELEGATE_APP; returns.*, afterSales.*, catalog.*, support.*, signup and legal only to the reseller apps. Back-office-only settings (stock rules, credit policy, numbering, quiet hours…) are never sent. Stock is never exposed as a number, so there is no stock display setting (STK-07). Platform constants are not settings and are not sent: OTP of 6 digits, 60 s resend cooldown, occurredAt accepted up to 72 hours old.
| Other block | Notes |
|---|---|
signup.businessTypes | The BUSINESS_TYPE reference list in the request language — sign-up happens before any session, so it cannot use reference.lists. customerTypes lists the offered values; labels come from the app's l10n (Super-gros, Demi-gros, Détail). |
legal | Sign-up consent (Law 18-07) and the re-acceptance check against Me.consents.termsVersion. Assumption — stored as settings legal.termsUrl, legal.privacyUrl, legal.termsVersion, to add to the catalogue. |
maintenance.until | AppVersionPolicy.maintenanceUntil (SRV-42): the expected end set on BO-66, null when none or when maintenance is off. Informative — the app keeps polling appConfig.get. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Unknown app or platform, version not semver. |
Rules & side effects
- Values come from
AppVersionPolicy(per app and platform, edited on BO-66 throughadmin.appVersions.crud) andSetting(edited on BO-64). Changes are visible within 5 minutes (cache). - This operation never answers
426or503: it is how an outdated or blocked app learns what to show. The other operations enforce the policy (AUTH-09):X-App-VersionbelowminVersion— or missing on a mobile session — →426 UPGRADE_REQUIRED(details.minVersion,details.storeUrl), exceptauth.logoutand, during the 72 hours after the minimum was raised,sync.batch(field work recorded offline is never stranded); maintenance →503 MAINTENANCE(details.until,details.message). Versions compare semantically (1.10.0is above1.9.3). Maintenance is per app — the back-office is never put in maintenance by this flag. - Clients call it on cold start, when resuming after more than an hour, and whenever any call answers
426or503 MAINTENANCE. The « Une mise à jour est disponible » prompt is shown at most once per day for a given version. serverTime: if the device clock differs by more than 5 minutes, the delegate app warns the user (offline actions are timestamped with the device clock).
Used by — splash RA-01, DA-01 and the web layout of RW-01; system states RA-36 and DA-28; sign-up form RA-05; consent DA-04.
GET /reference/wilayas
reference.wilayas · Access public (guests included) · Apps RA, RW, DA, BO · Idempotency-Key — · Release V1 · Requirements GEO-01
Purpose. Returns the official list of wilayas with French and Arabic names, for address forms, filters and zones.
Request
| Header | Notes |
|---|---|
If-None-Match | The ETag of the cached copy. |
Response — 200 OK with ETag: "geo-2026.1" and Cache-Control: public, max-age=86400; 304 Not Modified (empty body) when the copy is current. Not paginated, ordered by code.
{
"version": "geo-2026.1",
"items": [
{ "code": "01", "nameFr": "Adrar", "nameAr": "أدرار", "communeCount": 16 },
{ "code": "16", "nameFr": "Alger", "nameAr": "الجزائر", "communeCount": 57 },
{ "code": "31", "nameFr": "Oran", "nameAr": "وهران", "communeCount": 26 },
{ "code": "46", "nameFr": "Aïn Témouchent", "nameAr": "عين تموشنت", "communeCount": 28 }
]
}(Excerpt: the response lists every active wilaya of the seeded official reference — 69 since the 2026 territorial reorganisation, see the geography model.)
Errors — none specific.
Rules & side effects
- The data is the seeded official reference (
Wilaya, active rows only), changed only by a migration when the administrative division changes;version(=ETag) changes with it. - Clients store the list (shared preferences / drift) and revalidate at start-up; the display name follows the app language (
nameArin Arabic,nameFrotherwise).
Used by — wilaya pickers of RA-05, RA-19, DA-14, RW-01, the guest region picker of the WhatsApp button, and back-office filters and the zone editor BO-19.
GET /reference/wilayas/{wilayaCode}/communes
reference.communes · Access public (guests included) · Apps RA, RW, DA, BO · Idempotency-Key — · Release V1 · Requirements GEO-01
Purpose. Returns the communes of one wilaya, for the commune picker that follows the wilaya picker.
Request
| Path parameter | Type | Notes |
|---|---|---|
wilayaCode | string | Two digits, "31". |
If-None-Match as for wilayas.
Response — 200 OK, ETag, Cache-Control: public, max-age=86400; 304 when current. Ordered by French name.
{
"wilayaCode": "31",
"version": "geo-2026.1",
"items": [
{ "id": "01a0c3e1-5d20-7a10-8b31-310900000009", "code": "3109", "nameFr": "Aïn El Turk", "nameAr": "عين الترك" },
{ "id": "01a0c3e1-5d20-7a10-8b31-310600000006", "code": "3106", "nameFr": "Arzew", "nameAr": "أرزيو" },
{ "id": "01a0c3e1-5d20-7a10-8b31-310300000003", "code": "3103", "nameFr": "Bir El Djir", "nameAr": "بئر الجير" },
{ "id": "01a0c3e1-5d20-7a10-8b31-310500000005", "code": "3105", "nameFr": "Es Senia", "nameAr": "السانية" },
{ "id": "01a0c3e1-5d20-7a10-8b31-310100000001", "code": "3101", "nameFr": "Oran", "nameAr": "وهران" }
]
}(Excerpt.)
| Field | Notes |
|---|---|
id | The communeId used by addresses, sign-up and zones (Commune.id). |
code | Official commune code (wilaya code + 2-digit rank), informative — never use it as a key. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | wilayaCode is not two digits. |
| 404 | NOT_FOUND | No such wilaya. |
Rules & side effects — the delegate app downloads the communes of every wilaya of its zones at sign-in (from Me.delegate.zones[].wilayaCodes) so that prospects can be created offline (FLD-11).
Used by — commune pickers of RA-05, RA-19, DA-14, DA-15, RW-01, RW-12 and the back-office forms.
GET /reference/lists
reference.lists · Access session (any user type; lists filtered by user type) · Apps RA, RW, DA, BO · Idempotency-Key — · Release V1 · Requirements ADM-05
Purpose. Returns the option lists behind the apps' dropdowns and chips — business types, not-done reasons, issue tags, visit types, ticket types, payment methods… — in the caller's language, in the order configured by the back-office.
Request
| Query parameter | Required | Notes |
|---|---|---|
listKey | no | Repeatable (?listKey=VISIT_NOT_DONE_REASON&listKey=VISIT_ISSUE_TAG). Omitted → every list available to the caller. |
Accept-Language selects the labels; If-None-Match revalidates.
Response — 200 OK, ETag, Vary: Accept-Language, Cache-Control: private, max-age=3600; 304 when current.
interface ReferenceListsResponse {
version: string;
lists: {
listKey: string;
kind: "DATA" | "ENUM"; // DATA: editable items (labels are data); ENUM: an enum's offered values
items: {
code: string;
label: string; // localised
sortOrder: number;
meta?: Record<string, unknown>; // list-specific attributes, see the table
}[];
}[];
}{
"version": "ref-2026-11-02T16:40:00Z",
"lists": [
{
"listKey": "VISIT_NOT_DONE_REASON",
"kind": "DATA",
"items": [
{ "code": "SHOP_CLOSED", "label": "Magasin fermé", "sortOrder": 10 },
{ "code": "OWNER_ABSENT", "label": "Responsable absent", "sortOrder": 20 },
{ "code": "VISIT_REFUSED", "label": "Visite refusée", "sortOrder": 30 },
{ "code": "ADDRESS_NOT_FOUND", "label": "Adresse introuvable", "sortOrder": 40 },
{ "code": "RESCHEDULED", "label": "Reportée à la demande du client", "sortOrder": 50 },
{ "code": "OTHER", "label": "Autre (préciser)", "sortOrder": 90, "meta": { "requiresComment": true } }
]
},
{
"listKey": "VISIT_TYPE",
"kind": "DATA",
"items": [
{ "code": "PROSPECTION", "label": "Prospection", "sortOrder": 10, "meta": { "photoRequired": true, "reportRequired": true } },
{ "code": "FOLLOW_UP", "label": "Suivi commercial", "sortOrder": 20, "meta": { "photoRequired": false, "reportRequired": true } },
{ "code": "MERCHANDISING", "label": "Merchandising", "sortOrder": 30, "meta": { "photoRequired": true, "reportRequired": false } }
]
},
{
"listKey": "POTENTIAL",
"kind": "ENUM",
"items": [
{ "code": "LOW", "label": "Faible", "sortOrder": 10 },
{ "code": "MEDIUM", "label": "Moyen", "sortOrder": 20 },
{ "code": "HIGH", "label": "Élevé", "sortOrder": 30 }
]
}
]
}Available lists
listKey | Kind | Source | Available to | Notes |
|---|---|---|---|---|
BUSINESS_TYPE | DATA | ReferenceItem | all | Seed: PHONE_SHOP, ACCESSORIES_SHOP, MULTISERVICE, ELECTRONICS, WHOLESALER, OTHER. |
PERSON_ROLE | DATA | ReferenceItem | DA, BO | Person met in a visit report: OWNER, MANAGER, EMPLOYEE, OTHER. |
VISIT_NOT_DONE_REASON | DATA | ReferenceItem | DA, BO | meta.requiresComment (ReferenceItem.requiresComment, e.g. OTHER). |
VISIT_ISSUE_TAG | DATA | ReferenceItem | DA, BO | PRICE_TOO_HIGH, STOCK_SHORTAGE, DELIVERY_DELAY, PRODUCT_DEFECT, COMPETITION, PAYMENT_TERMS, NEEDS_DISPLAY, OTHER. |
TOUR_SUSPEND_REASON | DATA | ReferenceItem | DA, BO | BREAK, VEHICLE_ISSUE, WEATHER, MEETING, EMERGENCY, OTHER. |
ORDER_CANCEL_REASON | DATA | ReferenceItem | RA, RW, BO | ORDERED_BY_MISTAKE, CHANGE_QUANTITIES, DELAY_TOO_LONG, FOUND_ELSEWHERE, OTHER. |
ORDER_REFUSAL_REASON | DATA | ReferenceItem | BO | Used by the order confirmation queue. |
PAYMENT_REJECT_REASON | DATA | ReferenceItem | BO | Used when verifying declared payments. |
ASSISTED_REJECT_REASON | DATA | ReferenceItem | RA, RW, BO | WRONG_ITEMS, TOO_EXPENSIVE, NOT_REQUESTED, LATER, OTHER — orders.rejectAssisted. Contract 1.1.0 |
RETURN_REASON | DATA | ReferenceItem | RA, RW, BO | DAMAGED_ON_DELIVERY, DEFECTIVE, WRONG_ITEM, NOT_AS_DESCRIBED, OVERSTOCK, OTHER — returns.create. Contract 1.1.0 |
VISIT_TYPE | DATA | VisitType | DA, BO | meta.photoRequired, meta.reportRequired (FLD-08, FLD-10). |
POTENTIAL | ENUM | Potential | DA, BO | |
INTEREST_LEVEL | ENUM | InterestLevel | DA, BO | |
NEXT_ACTION | ENUM | NextAction | DA, BO | |
TASK_TYPE | ENUM | TaskType | DA, BO | |
PHOTO_KIND | ENUM | PhotoKind | RA, RW, DA, BO | |
DOCUMENT_TYPE | ENUM | DocumentType | RA, RW, DA, BO | |
TICKET_TYPE | ENUM | TicketType | RA, RW, BO | Includes AFTER_SALES (SUP-04). |
AFTER_SALES_ISSUE | ENUM | AfterSalesIssue | RA, RW, BO | Issue of an after-sales claim. |
CUSTOMER_TYPE | ENUM | CustomerType | DA, BO | Delegates may record a declared type on a prospect. |
PAYMENT_METHOD | ENUM | PaymentMethod | RA, RW, DA, BO | Only the methods enabled in the business settings. |
Errors
| Status | Code | When |
|---|---|---|
| 400 | VALIDATION_FAILED | Unknown listKey (field code INVALID_ENUM). |
| 403 | PERMISSION_DENIED | A requested list is not available to the caller's user type (details.listKey). |
Rules & side effects
DATAlists are edited on BO-65 (admin.referenceLists.crud) and visit types on BO-26; labels are stored per language and fall back to French. The seed codes above are proposals to validate with HOCO.ENUMlists exist so that dropdowns follow the configuration (enabled payment methods, order); their labels equal the apps' l10n labels fromenums.json. Resource fields still carry enum values only — the "no labels on enum fields" rule of the conventions is unchanged.- Only active items are returned. Records keep the code of an item deactivated later; clients show the code's last known label (from their cache) or the raw code.
- Any change to a list changes
version. The delegate app stores the lists in its local database for offline use and revalidates at sign-in and once a day.
Used by — DA-11 (not-done reasons), DA-12 (person met, interest, issues, next action), DA-14 (business type, potential, customer type), DA-06 (suspend reasons); RA-22 (cancel reasons), RA-28 (document types), RA-31 (payment methods), RA-33 (ticket types and after-sales issues); the equivalent RW pages and back-office forms.
POST /me/deletion-request
me.deleteAccount · Access session (reseller) · Apps RA, RW · Idempotency-Key required · Release V1 · Requirements SEC-08, AUTH-06
Purpose — Google Play requires an in-app way to request account deletion. The reseller confirms with their password; the account is closed and personal data is anonymised once the legal retention period of business records (orders, invoices) has passed.
Request
interface DeletionRequestBody {
password: string;
reason?: string; // free text, ≤ 500 chars
}{ "password": "••••••••", "reason": "Je ferme mon magasin." }Response 202 Accepted
{ "status": "SCHEDULED", "accountStatus": "DISABLED", "anonymiseAfter": "2036-12-31" }Errors
| Status | Code | When |
|---|---|---|
| 401 | INVALID_CURRENT_PASSWORD | Wrong password |
| 409 | OPEN_ORDERS_EXIST | Orders not yet DELIVERED, CANCELLED or REFUSED, or an open return/claim |
Rules & side effects
- Every session is revoked, devices unregistered, the account set to
DISABLED(history kept — SEC-06). - Contact data is anonymised by the retention job when the retention period of the linked financial records ends (cahier §10, Law 18-07 — durations to validate with HOCO's counsel).
- Audit entry
DELETEonUser; the back-office is notified (account.deletion_requested).
Authentication & sessions
Sign-in for the four clients, OTP codes, reseller sign-up with customer type and account claim, password reset and change, guest access and session management.
File uploads & downloads
Two-step uploads to S3-compatible storage with pre-signed URLs, per-purpose limits, image processing (EXIF stripping, thumbnails) and the rules for reading private files.