# Menus de la boutique

Le menu de la boutique est le choix de liens du marchand pour la barre de navigation, le menu latéral sur téléphone et le pied de page. L'API le lit et l'écrit en un seul document : jusqu'à 12 liens en haut avec jusqu'à 10 sous-liens chacun, jusqu'à 3 groupes de 10 liens dans le pied de page, et un interrupteur pour le grand panneau des catégories. Une boutique qui n'a jamais enregistré de menu affiche les liens automatiques, et ces deux endpoints lui répondent `automatic: true`.

Le marchand modifie le même document dans le tableau de bord, dans Personnaliser, ligne **Menu et liens**. Voir [Menu et liens du pied de page](https://dzbuild.com/fr/docs/storefront/menus) dans la documentation marchand.

## Avant de commencer[​](#avant-de-commencer "Lien direct vers Avant de commencer")

* Les lectures demandent `store:read` et les écritures `store:write`. Les clés marchand portent les deux.
* `PUT` remplace le document entier. Lisez-le d'abord, changez ce qu'il faut, puis renvoyez-le avec la `version` lue.
* `Idempotency-Key` est facultatif sur `PUT`. Un `PUT` avec une clé est rejoué pendant 24 heures. Voir [Idempotency](https://dzbuild.com/fr/fr/api-docs/idempotency.md).

## Le document du menu[​](#le-document-du-menu "Lien direct vers Le document du menu")

| Champ    | Type             | Notes                                                                                                                                                                                                 |
| -------- | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `v`      | int              | Toujours `1`.                                                                                                                                                                                         |
| `mega`   | boolean          | `true` active le grand panneau des catégories. Voir [Le grand panneau](#the-wide-panel).                                                                                                              |
| `top`    | array d'éléments | Les liens de la barre de navigation, dans l'ordre. De 1 à 12. `home`, `products`, `categories`, `track`, `contact` et `cart` y apparaissent au plus une fois chacun ; les `children` ne comptent pas. |
| `footer` | array de groupes | Les colonnes du pied de page, dans l'ordre. Jusqu'à 3.                                                                                                                                                |

### Éléments[​](#éléments "Lien direct vers Éléments")

| Champ                | Type             | Notes                                                                                                                                                                                  |
| -------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `type`               | string           | Un de `home`, `products`, `categories`, `category`, `page`, `track`, `contact`, `cart`, `url`. Voir [Types d'éléments](#item-types).                                                   |
| `label`              | string           | Jusqu'à 40 caractères. Vide veut dire le nom de la cible elle-même, dans la langue du visiteur pour les types prévus. Obligatoire pour `url`.                                          |
| `ref`                | int ou null      | L'id de la catégorie pour `category`, l'id de la page pour `page`, `null` pour les autres types.                                                                                       |
| `url`                | string           | L'adresse pour `url`, jusqu'à 500 octets. Vide pour les autres types.                                                                                                                  |
| `show_subcategories` | boolean          | Éléments `category` du haut seulement : affiche aussi jusqu'à 10 sous-catégories de cette catégorie. Enregistré `false` partout ailleurs, `children` et liens du pied de page compris. |
| `children`           | array d'éléments | Éléments du haut seulement : jusqu'à 10 sous-liens. Un sous-lien n'a pas de `children` à lui et ne peut pas être un élément `categories`.                                              |

### Groupes[​](#groupes "Lien direct vers Groupes")

| Champ   | Type             | Notes                                                                                                                                                                                                                                    |
| ------- | ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `key`   | string ou null   | `quick_links` ou `categories` pour les deux groupes automatiques, `null` pour un groupe que vous ajoutez.                                                                                                                                |
| `label` | string           | Le titre de la colonne, jusqu'à 40 caractères. Il peut être vide seulement quand `key` est présent : la boutique affiche alors le titre automatique dans la langue du visiteur. Un titre écrit s'affiche pareil dans toutes les langues. |
| `links` | array d'éléments | De 1 à 10 liens. Les liens du pied de page n'ont ni `children` ni `show_subcategories`.                                                                                                                                                  |

Les libellés sont enregistrés en texte simple : les balises HTML sont retirées et les espaces aux bords supprimés. Le document entier fait au plus 16 KB.

### Types d'éléments[​](#item-types "Lien direct vers Types d'éléments")

| `type`       | Le lien ouvre                                                                                                                                                                   |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `home`       | `/`, l'accueil de la boutique.                                                                                                                                                  |
| `products`   | `/products`, tous les produits.                                                                                                                                                 |
| `categories` | Une liste déroulante des catégories principales et de leurs sous-catégories, avec une ligne tous les produits. Dans le pied de page, la partie catégories de la page d'accueil. |
| `category`   | `/category/{ref}`. `ref` doit être une catégorie de cette boutique.                                                                                                             |
| `page`       | `/pages/{slug}` de la page `ref`. `ref` doit être une page publiée de cette boutique. Voir [Pages de la boutique](https://dzbuild.com/fr/fr/api-docs/resources/pages.md).       |
| `track`      | `/track-order`.                                                                                                                                                                 |
| `contact`    | La partie contact de la page d'accueil.                                                                                                                                         |
| `cart`       | `/cart`.                                                                                                                                                                        |
| `url`        | `url` : un chemin de la boutique comme `/pages/delivery`, une ancre comme `#products`, une adresse `http://` ou `https://`, `tel:` ou `mailto:`.                                |

Une `url` sans protocole qui ressemble à un domaine, comme `instagram.com/x`, est enregistrée `https://instagram.com/x`. `javascript:`, `data:`, `//`, les barres obliques inverses, les guillemets, les espaces et les chevrons sont refusés avec `bad_url`.

Sur la boutique, un lien vers une catégorie supprimée depuis, ou vers une page remise en brouillon, est retiré avec ses sous-liens. Une catégorie inactive n'est pas affichée.

### Le grand panneau[​](#the-wide-panel "Lien direct vers Le grand panneau")

Avec `"mega": true`, un élément du haut dont les sous-liens ont leurs propres sous-liens s'affiche sur ordinateur en grand panneau, avec une colonne par sous-lien. C'est le cas de l'élément `categories` quand une catégorie principale a des sous-catégories. Un élément `category` avec `show_subcategories` liste jusqu'à 10 sous-catégories, et avec `mega` jusqu'à 10 de leurs propres sous-catégories chacune, un troisième niveau. L'élément `categories` garde les limites du menu automatique : 10 catégories principales avec 5 sous-catégories chacune dans la barre de navigation, 12 avec 6 sur le thème Promobi. Sur téléphone, le menu latéral montre les mêmes niveaux en accordéon.

## `GET /v1/store/menus`[​](#get-v1storemenus "Lien direct vers get-v1storemenus")

Le menu de la boutique. Pour une boutique qui n'en a jamais enregistré, `menus` est le menu automatique et `automatic` vaut `true`.

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

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

```
curl 'https://api.dzbuild.app/v1/store/menus' \

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

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

`version` est le jeton à renvoyer avec `PUT`. `max` donne les limites et `types` les types d'éléments acceptés.

```
{

  "data": {

    "automatic": false,

    "menus": {

      "v": 1,

      "mega": false,

      "top": [

        {"type": "home", "label": "", "ref": null, "url": "", "show_subcategories": false, "children": []},

        {"type": "category", "label": "", "ref": 152, "url": "", "show_subcategories": true, "children": []},

        {"type": "page", "label": "", "ref": 7, "url": "", "show_subcategories": false, "children": []}

      ],

      "footer": [

        {"label": "Infos", "links": [{"type": "track", "label": "", "ref": null, "url": ""}, {"type": "url", "label": "Instagram", "ref": null, "url": "https://instagram.com/my-store"}]}

      ]

    },

    "version": "3f9a1c07d2e4b851",

    "max": {"top": 12, "children": 10, "groups": 3, "links": 10, "label": 40},

    "types": ["home", "products", "category", "page", "track", "contact", "cart", "url", "categories"]

  },

  "meta": {"request_id": "5d2a8e1f07c3b946", "api_version": "v1"}

}
```

## `PUT /v1/store/menus`[​](#put-v1storemenus "Lien direct vers put-v1storemenus")

Remplace le menu entier.

**Auth :** clé plateforme avec `store:write`. `Idempotency-Key` facultatif.

### Corps[​](#corps "Lien direct vers Corps")

| Champ     | Type           | Obligatoire | Notes                                                                                                                                                                  |
| --------- | -------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `menus`   | object ou null | oui         | Le nouveau document. `null` remet le menu automatique.                                                                                                                 |
| `version` | string         | non         | La `version` que vous avez lue. Quand elle ne correspond plus, l'appel répond `409 write_conflict` et ne change rien. Sans elle, l'appel écrase ce qui est enregistré. |

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

```
curl -X PUT 'https://api.dzbuild.app/v1/store/menus' \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Content-Type: application/json" \

  -H "Idempotency-Key: menus-sizes-1" \

  -d '{"version": "3f9a1c07d2e4b851", "menus": {"v": 1, "mega": false, "top": [{"type": "home", "label": "", "ref": null, "url": "", "show_subcategories": false, "children": []}, {"type": "page", "label": "Guide des tailles", "ref": 9, "url": "", "show_subcategories": false, "children": []}], "footer": []}}'
```

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

`menus` est le document tel qu'enregistré, `version` le nouveau jeton et `change_id` l'id dans le journal des changements. Un corps qui ne change rien répond `change_id: null`.

```
{

  "data": {

    "menus": {

      "v": 1,

      "mega": false,

      "top": [

        {"type": "home", "label": "", "ref": null, "url": "", "show_subcategories": false, "children": []},

        {"type": "page", "label": "Guide des tailles", "ref": 9, "url": "", "show_subcategories": false, "children": []}

      ],

      "footer": []

    },

    "version": "b70e4d2a9c15f383",

    "change_id": 90420,

    "automatic": false

  },

  "meta": {"request_id": "a81c3f5e92d07b64", "api_version": "v1"}

}
```

`"menus": null` répond `automatic: true` avec le menu automatique dans `menus`.

## Annuler[​](#annuler "Lien direct vers Annuler")

`PUT` répond avec un `change_id`. `POST /v1/changes/{change_id}/undo` remet le menu tel qu'il était avant cet appel, menu automatique compris. L'annulation demande `store:write` et un `Idempotency-Key`.

* Quand le menu a encore changé depuis cet appel, l'annulation répond `409 menus_changed` et ne change rien. Annulez d'abord le changement plus récent.
* Un changement annulé une deuxième fois répond `409 already_undone`.
* Les menus que le marchand enregistre dans Personnaliser ne sont pas enregistrés dans le journal, donc on ne peut pas les annuler par l'API.
* `GET /v1/changes?entity=store.menus` liste les changements du menu, du plus récent au plus ancien, avec `store:read`. `entity_id` vaut `menus`.
* Le jeton d'une application installée ne peut ni lire ni annuler les changements : `GET /v1/changes` et l'annulation répondent `403 forbidden`. Les réponses d'écriture portent quand même le `change_id`.

```
curl -X POST 'https://api.dzbuild.app/v1/changes/90420/undo' \

  -H "Authorization: Bearer $DZ_KEY" \

  -H "Idempotency-Key: undo-90420"
```

## Erreurs[​](#erreurs "Lien direct vers Erreurs")

| HTTP | Code                    | Cause                                                                                                                                                                                                                                                                                                        |
| ---- | ----------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| 400  | `bad_request`           | Le corps n'est pas un objet JSON, `menus` manque ou n'est ni un objet ni `null`, ou `version` n'est pas un texte.                                                                                                                                                                                            |
| 401  | `unauthorized`          | Clé mauvaise ou absente.                                                                                                                                                                                                                                                                                     |
| 402  | `quota_exceeded`        | Le quota mensuel de requêtes de la boutique est épuisé. Voir [Limites de taux](https://dzbuild.com/fr/fr/api-docs/rate-limits.md).                                                                                                                                                                           |
| 403  | `forbidden`             | `Missing scope: store:read` ou `Missing scope: store:write`, ou `API access requires an active Enterprise plan` pour une clé marchand dont la boutique n'a pas de plan Enterprise actif.                                                                                                                     |
| 404  | `not_found`             | La boutique n'existe pas.                                                                                                                                                                                                                                                                                    |
| 409  | `write_conflict`        | La `version` envoyée ne correspond plus : l'erreur porte les `menus` actuels (`null` tant que la boutique affiche les liens automatiques) et la `version` actuelle, pour fusionner et réessayer. Le même code sans eux veut dire qu'une autre écriture est arrivée au même moment : relisez, puis réessayez. |
| 409  | `menus_changed`         | Annulation seulement : le menu a changé depuis le changement que vous annulez.                                                                                                                                                                                                                               |
| 409  | `already_undone`        | Annulation seulement : le changement a déjà été annulé.                                                                                                                                                                                                                                                      |
| 413  | `payload_too_large`     | Le corps dépasse 1 MB.                                                                                                                                                                                                                                                                                       |
| 422  | `invalid_menus`         | Une valeur a été refusée. `fields` donne `{path, code}` pour elle. Voir les codes ci-dessous.                                                                                                                                                                                                                |
| 422  | `idempotency_key_reuse` | Le même `Idempotency-Key` a servi avec un autre corps.                                                                                                                                                                                                                                                       |
| 429  | `rate_limited`          | Trop de requêtes. Attendez `Retry-After`.                                                                                                                                                                                                                                                                    |
| 500  | `server_error`          | La requête a échoué. Réessayez avec le même `Idempotency-Key`.                                                                                                                                                                                                                                               |

`path` commence par `menus.` et montre la valeur refusée, comme `menus.top.0.url` ou `menus.footer.1.links.10` ; il vaut `menus` seul pour `too_large`. Le `code` est un de ceux-ci.

| `code`             | Cause                                                                                                                                            |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------ |
| `bad_url`          | Une `url` vide, refusée ou trop longue.                                                                                                          |
| `invalid`          | Une erreur de forme : une liste là où un objet est attendu ou l'inverse, comme un `top` qui n'est pas un array.                                  |
| `too_many`         | Plus de 12 éléments en haut, de 10 sous-liens, de 3 groupes ou de 10 liens dans un groupe. `path` nomme le premier élément au-delà de la limite. |
| `top_required`     | `top` est vide. Pour abandonner un menu personnalisé, envoyez `"menus": null`.                                                                   |
| `unknown_type`     | `type` n'est pas un des neuf types.                                                                                                              |
| `unknown_key`      | Une `key` de groupe du pied de page autre que `quick_links`, `categories` ou `null`.                                                             |
| `unknown_category` | `ref` n'est pas une catégorie de cette boutique.                                                                                                 |
| `unknown_page`     | `ref` n'est pas une page publiée de cette boutique.                                                                                              |
| `label_required`   | Un élément `url` sans libellé, ou un groupe du pied de page sans libellé et sans `key`.                                                          |
| `label_too_long`   | Un libellé de plus de 40 caractères.                                                                                                             |
| `links_required`   | Un groupe du pied de page sans lien.                                                                                                             |
| `duplicate_type`   | Un deuxième élément `home`, `products`, `categories`, `track`, `contact` ou `cart` dans `top`.                                                   |
| `nested_too_deep`  | Des `children` dans un sous-lien ou un lien du pied de page, ou un élément `categories` comme sous-lien.                                         |
| `too_large`        | Le document dépasse 16 KB.                                                                                                                       |

```
{

  "error": {

    "code": "invalid_menus",

    "message": "Invalid menu at menus.top.0.url (bad_url); the caps and types are in GET /v1/store/menus.",

    "fields": [{"path": "menus.top.0.url", "code": "bad_url"}]

  },

  "meta": {"request_id": "c27e90b4f13a5d68", "api_version": "v1"}

}
```

## Limites[​](#limites "Lien direct vers Limites")

* **Menu du haut** : 12 éléments, chacun avec jusqu'à 10 sous-liens.
* **Pied de page** : 3 groupes de 10 liens au plus.
* **Libellé** : 40 caractères. **Url** : 500 octets. **Document** : 16 KB.
* Les limites sont les mêmes sur tous les plans.

## Outils MCP[​](#outils-mcp "Lien direct vers Outils MCP")

Le serveur MCP, version 1.9.0, donne à un assistant IA deux outils de menu avec les mêmes règles : `menus_get` renvoie le document entier avec sa `version` et les limites, `menus_set` le réécrit avec cette `version`, et `change_undo` annule une écriture.
