# Clients

Les clients sont les utilisateurs finaux qui ont passé une commande sur votre vitrine. Ils sont créés automatiquement au premier checkout — il n'y a pas de flux d'inscription public en v1. Chaque client est **scopé à une seule boutique** : le même numéro de téléphone sur deux boutiques différentes crée deux lignes `customer`.

Déduplication : à chaque nouveau checkout, on rapproche sur boutique + téléphone. Si le client existe déjà, son email/adresse/wilaya sont mis à jour et sa fiche réutilisée ; sinon une nouvelle fiche est créée. **L'email n'est pas utilisé pour la déduplication** — historiquement, les marchands reçoivent beaucoup de commandes anonymes sans email.

Quand un client est créé via `POST /v1/orders`, la chaîne `customer.name` entière est stockée dans `first_name` et `last_name` est laissé vide — la séparation prénom/nom montrée dans les exemples ci-dessous ne se produit jamais pour les enregistrements créés par l'API. Pour un client existant retrouvé par téléphone, seul `first_name` est écrasé et tout `last_name` existant reste intact. Séparez côté client si vous en avez besoin.

## `GET /v1/customers`[​](#get-v1customers "Lien direct vers get-v1customers")

Liste les clients de votre boutique. Pagination par curseur.

**Auth :** clé plateforme avec `customers:read`, que les nouvelles clés portent par défaut. Sans ce scope, l'appel répond `403 forbidden`.

### Paramètres de requête[​](#paramètres-de-requête "Lien direct vers Paramètres de requête")

| Param    | Type      | Notes                                                                                                                                                                                                                                                                                                            |
| -------- | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `limit`  | int 1–200 | Défaut 50                                                                                                                                                                                                                                                                                                        |
| `cursor` | string    | Opaque                                                                                                                                                                                                                                                                                                           |
| `phone`  | string    | Match exact sur le numéro enregistré. Les checkouts vitrine et landing page enregistrent les mobiles algériens au format `0XXXXXXXXX` ; les numéros venus de `POST /v1/orders` et des commandes manuelles du tableau de bord sont enregistrés tels qu'envoyés, essayez donc l'autre format si rien ne correspond |
| `email`  | string    | Match exact, insensible à la casse                                                                                                                                                                                                                                                                               |

Il n'existe en v1 aucun filtre par date, recherche libre, tri ni filtre `is_banned` ; les résultats sont toujours triés par id décroissant.

### Requête[​](#requête "Lien direct vers Requête")

```
curl 'https://api.dzbuild.app/v1/customers?limit=20' \

  -H "Authorization: Bearer $DZ_KEY"
```

### Réponse 200[​](#réponse-200 "Lien direct vers Réponse 200")

```
{

  "data": {

    "items": [

      {

        "id":           5578,

        "first_name":   "John",

        "last_name":    "Doe",

        "phone":        "0555000000",

        "email":        null,

        "wilaya_id":    16,

        "commune":      "Bab Ezzouar",

        "total_orders": 3,

        "total_spent":  4500,

        "is_banned":    false,

        "created_at":   "2026-03-17 15:18:13",

        "updated_at":   "2026-04-15 12:01:08"

      }

    ],

    "next_cursor": "NDk=",

    "has_more": true

  }

}
```

`total_orders` / `total_spent` sont des compteurs figés, pas des agrégats en temps réel

Ils ne sont incrémentés que lorsqu'une commande est passée depuis une landing page ou créée manuellement dans le tableau de bord. Ils ne sont jamais ajustés lors d'un changement de statut (une commande annulée reste comptée), et les commandes créées via la vitrine ou `POST /v1/orders` ne les mettent jamais à jour. Ne les utilisez pas pour du reporting de chiffre d'affaires — agrégez vous-même `GET /v1/orders` (ou `/v1/customers/{id}/orders`).

## `GET /v1/customers/{id}`[​](#get-v1customersid "Lien direct vers get-v1customersid")

Détail complet.

**Auth :** clé plateforme avec `customers:read`.

### Réponse 200[​](#réponse-200-1 "Lien direct vers Réponse 200")

```
{

  "data": {

    "id":           5578,

    "first_name":   "John",

    "last_name":    "Doe",

    "phone":        "0555000000",

    "email":        "john@example.com",

    "wilaya_id":    16,

    "commune":      "Bab Ezzouar",

    "address":      "12 Rue X",

    "notes":        null,

    "total_orders": 3,

    "total_spent":  4500,

    "fraud_score":  0,

    "is_banned":    false,

    "created_at":   "2026-03-17 15:18:13",

    "updated_at":   "2026-04-15 12:01:08"

  }

}
```

| Champ                           | Notes                                                                                                                                                                                                                                                                                                                      |
| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `notes`                         | Réservé. Ni le tableau de bord ni l'API ne l'écrivent : attendez-vous à `null` pour la plupart des clients.                                                                                                                                                                                                                |
| `fraud_score`                   | **Toujours `0` en v1** — le champ est réservé et n'est jamais renseigné via l'API. L'indicateur de risque visible sur la page Clients du tableau de bord n'est pas exposé ici. Ne construisez aucune logique anti-fraude sur ce champ.                                                                                     |
| `is_banned`                     | Défini quand vous blacklistez le client dans le tableau de bord. Les checkouts vitrine et landing page rejettent les clients blacklistés — mais **`POST /v1/orders` ne consulte pas la blacklist**, donc les commandes créées par l'API passent pour un client blacklisté. Vérifiez `is_banned` vous-même avant d'envoyer. |
| Signaux d'appareil / d'identité | **Non exposés via API** pour des raisons de confidentialité                                                                                                                                                                                                                                                                |

## `GET /v1/customers/{id}/orders`[​](#get-v1customersidorders "Lien direct vers get-v1customersidorders")

Commandes du client, paginées par curseur, plus récentes d'abord.

**Auth :** clé plateforme avec `customers:read`.

L'id client est d'abord contrôlé en propriété : un id inconnu, ou appartenant à une autre boutique, renvoie `404 not_found`, pas une liste vide.

### Requête[​](#requête-1 "Lien direct vers Requête")

```
curl 'https://api.dzbuild.app/v1/customers/5578/orders?limit=10' \

  -H "Authorization: Bearer $DZ_KEY"
```

### Réponse 200[​](#réponse-200-2 "Lien direct vers Réponse 200")

```
{

  "data": {

    "items": [

      { "id": 6894, "order_number": "ORD-13-20260317-AD3C91F7", "status": "confirmed",

        "payment_status": "pending", "total": 1000, "created_at": "2026-03-17 15:18:13" }

    ],

    "next_cursor": null,

    "has_more": false

  }

}
```

C'est un **sous-ensemble** de `/v1/orders` filtré par `customer_id`, avec un résumé plus court : `id`, `order_number`, `status`, `payment_status`, `total` et `created_at` seulement. Pour le corps complet, appelez `/v1/orders/{id}`.

## Patterns courants[​](#patterns-courants "Lien direct vers Patterns courants")

### « Trouver un client par téléphone, puis lister ses commandes »[​](#-trouver-un-client-par-téléphone-puis-lister-ses-commandes- "Lien direct vers « Trouver un client par téléphone, puis lister ses commandes »")

```
PHONE="0555000000"

CUST=$(curl -sS "https://api.dzbuild.app/v1/customers?phone=$PHONE&limit=1" \

  -H "Authorization: Bearer $DZ_KEY" | jq -r '.data.items[0].id')



[ -z "$CUST" ] && { echo "no customer"; exit 1; }



curl -sS "https://api.dzbuild.app/v1/customers/$CUST/orders" \

  -H "Authorization: Bearer $DZ_KEY"
```

### « Top 10 dépensiers ce mois-ci »[​](#-top-10-dépensiers-ce-mois-ci- "Lien direct vers « Top 10 dépensiers ce mois-ci »")

Pas de filtre tri-par-dépense natif en v1, et `total_spent` est un compteur figé (voir l'avertissement plus haut). Requêtez `/v1/orders?since=...` et additionnez `total` par `customer_phone` : le résumé de la liste porte le téléphone, pas l'id client. Écartez les commandes `cancelled` et `returned` si vous ne voulez que les ventes conservées.

## Notes de confidentialité[​](#notes-de-confidentialité "Lien direct vers Notes de confidentialité")

* L'API n'expose jamais d'empreinte d'appareil ni aucun lien d'identité inter-boutiques.
* L'email/téléphone client sont des données personnelles — soyez prudent avec les logs.
* Les futures demandes de droit à l'oubli (style RGPD) doivent passer par dashboard / support ; l'API recevra un endpoint `DELETE /v1/customers/{id}` dans une version future avec des règles de cascade explicites.
