Codes promo
Ces quatre endpoints gèrent les codes promo de la boutique, les codes que l'acheteur saisit au moment de commander pour payer moins. Ils travaillent sur les mêmes codes que la page des codes promo du dashboard, décrite dans Codes de réduction, et ils peuvent aussi renommer un code, ce que le dashboard ne permet pas.
La réduction d'un code s'applique au sous-total des produits, jamais à la livraison. Un code percentage prend ce pourcentage du sous-total. Un code fixed soustrait son montant en DZD et ne retire jamais plus que le sous-total. Un code ne peut pas être limité à un produit, une catégorie ou un client, ne peut pas plafonner la réduction sur un gros panier et ne peut pas offrir la livraison gratuite.
Avant de commencer
- L'add-on Codes promotionnels doit être activé sur la boutique (
/dashboard/addons). La liste fonctionne sans lui et l'indique dansaddon_enabled. Toute écriture répond409 addon_inactivetant que l'add-on est inactif, et les acheteurs ne peuvent utiliser aucun code au moment de commander pendant ce temps. - La clé a besoin des portées des codes promo. Les clés créées depuis le dashboard (
/dashboard/api) ont les deux. Les portées sont figées à la création de la clé : une clé qui ne les a pas répond403 forbidden. Créez une nouvelle clé depuis le dashboard. starts_atetexpires_atsont à l'heure de l'Algérie, au formatYYYY-MM-DD HH:MM:SS.
| Portée | Description |
|---|---|
promos:read | Consulter les codes promo. |
promos:write | Créer, modifier et supprimer des codes promo. Cela change les prix payés par vos acheteurs. |
L'objet code promo
| Champ | Type | Signification |
|---|---|---|
id | int | L'id du code dans la boutique. |
code | string | Ce que l'acheteur saisit. En majuscules, A-Z, 0-9, - et _, unique dans la boutique. |
discount_type | string | percentage ou fixed. |
discount_value | number | Le pourcentage (supérieur à 0, au plus 100) ou le montant en DZD. |
min_order_amount | number ou null | Le sous-total produits qu'une commande doit atteindre pour que le code s'applique. null veut dire aucun minimum. |
max_uses | int ou null | Combien de commandes peuvent utiliser le code. null veut dire illimité. |
used_count | int | Combien de commandes l'ont utilisé. En lecture seule. |
is_active | bool | Un code inactif est refusé au moment de commander. |
starts_at | string ou null | Avant cette date, le code est refusé. null veut dire qu'il fonctionne tout de suite. |
expires_at | string ou null | Après cette date, le code est refusé. null veut dire qu'il n'expire jamais. |
created_at, updated_at | string | YYYY-MM-DD HH:MM:SS, heure du serveur. |
GET /v1/promo-codes
Les codes de la boutique, du plus récent au plus ancien. Aucun appel ne lit un seul code par son id : parcourez cette liste.
Auth : clé plateforme avec promos:read.
Paramètres de requête
| Param | Type | Défaut | Notes |
|---|---|---|---|
is_active | string | aucun | true, 1, yes ou on renvoie les codes actifs. Toute autre valeur renvoie les codes inactifs. Omettez-le pour avoir tous les codes. |
limit | int | 50 | De 1 à 200. |
cursor | string | aucun | Le next_cursor de la page précédente. Voir Pagination. |
Requête
curl 'https://api.dzbuild.app/v1/promo-codes?is_active=true' \
-H "Authorization: Bearer $DZ_KEY"
Réponse 200
{
"data": {
"items": [
{
"id": 20,
"code": "WELCOME10",
"discount_type": "percentage",
"discount_value": 10,
"min_order_amount": 3000,
"max_uses": 100,
"used_count": 7,
"is_active": true,
"starts_at": null,
"expires_at": "2026-11-30 23:59:00",
"created_at": "2026-10-06 14:20:11",
"updated_at": "2026-10-06 14:20:11"
}
],
"next_cursor": null,
"has_more": false,
"addon_enabled": true
}
}
addon_enabled indique si l'add-on Codes promotionnels est activé. Quand il vaut false, la liste répond toujours, mais les écritures échouent et les acheteurs ne peuvent pas utiliser les codes.
POST /v1/promo-codes
Crée un code. Il fonctionne au moment de commander dès sa création, sauf si vous envoyez is_active: false ou un starts_at dans le futur.
Auth : clé plateforme avec promos:write. Nécessite Idempotency-Key.
Corps
| Champ | Type | Requis | Notes |
|---|---|---|---|
code | string | oui | Les espaces autour sont retirés et les lettres passent en majuscules, puis il doit faire de 2 à 30 caractères parmi A-Z, 0-9, - ou _. Un code qui existe déjà dans la boutique répond 409 code_exists. |
discount_type | string | oui | percentage ou fixed, en majuscules ou en minuscules. Il n'y a pas de valeur par défaut. |
discount_value | number | oui | Supérieur à 0. Au plus 100 pour percentage, et 100 demande la confirmation du marchand (voir la section sur la réduction de 100% plus bas). Aucune limite haute pour fixed. |
min_order_amount | number ou null | non | Sous-total produits minimum en DZD. 0, "" ou null veut dire aucun minimum. |
max_uses | int ou null | non | 1 ou plus. 0, "" ou null veut dire illimité. |
is_active | bool | non | true par défaut. Envoyez un booléen JSON : une chaîne comme "false" est lue comme true. |
starts_at | string ou null | non | Un format courant de date et d'heure, comme 2026-11-01 08:00 ou une chaîne ISO 8601. Enregistré au format YYYY-MM-DD HH:MM:SS à l'heure de l'Algérie ; une valeur avec un décalage UTC est convertie. null ou "" veut dire aucune date de début. |
expires_at | string ou null | non | Mêmes formats. Doit être dans le futur et après starts_at. null ou "" veut dire aucune expiration. |
confirm_token | string | non | Seulement pour une réduction de 100%. |
confirm_full_discount | bool | non | Seulement pour une réduction de 100%. |
Requête
curl -X POST 'https://api.dzbuild.app/v1/promo-codes' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: promo-welcome10-create" \
-d '{"code": "welcome10", "discount_type": "percentage", "discount_value": 10, "min_order_amount": 3000, "max_uses": 100, "expires_at": "2026-11-30 23:59"}'
Réponse 201
{
"data": {
"id": 20,
"code": "WELCOME10",
"discount_type": "percentage",
"discount_value": 10,
"min_order_amount": 3000,
"max_uses": 100,
"used_count": 0,
"is_active": true,
"starts_at": null,
"expires_at": "2026-11-30 23:59:00",
"created_at": "2026-10-06 14:20:11",
"updated_at": "2026-10-06 14:20:11"
}
}
PATCH /v1/promo-codes/{id}
Change uniquement les champs que vous envoyez, avec les mêmes règles que la création. Un champ omis garde sa valeur.
Auth : clé plateforme avec promos:write. Nécessite Idempotency-Key.
codepeut être changé. Le nouveau texte doit être libre dans la boutique, sinon l'appel répond409 code_exists.discount_typeetdiscount_valuesont vérifiés ensemble. Envoyer seulementdiscount_type: "percentage"sur un codefixedde 500 DZD répond422 invalid_discount_value, parce que 500 dépasse 100. Envoyez les deux.- Un nouveau
expires_atdoit être dans le futur. Un code déjà expiré reste modifiable tant que vous n'envoyez pasexpires_at. used_countne peut pas être écrit.- Un corps vide ne change rien et renvoie le code.
- Un code d'une autre boutique répond
404, comme un code qui n'existe pas.
Requête
curl -X PATCH 'https://api.dzbuild.app/v1/promo-codes/20' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: promo-20-extend" \
-d '{"max_uses": 200, "expires_at": "2026-12-31 23:59"}'
Réponse 200
{
"data": {
"id": 20,
"code": "WELCOME10",
"discount_type": "percentage",
"discount_value": 10,
"min_order_amount": 3000,
"max_uses": 200,
"used_count": 7,
"is_active": true,
"starts_at": null,
"expires_at": "2026-12-31 23:59:00",
"created_at": "2026-10-06 14:20:11",
"updated_at": "2026-10-20 09:05:42"
}
}
DELETE /v1/promo-codes/{id}
Supprime le code : les acheteurs ne peuvent plus l'utiliser. Les commandes déjà passées avec lui gardent leur réduction. Pour mettre un code en pause sans perdre son nombre d'utilisations, envoyez plutôt is_active: false avec PATCH.
Auth : clé plateforme avec promos:write. Nécessite Idempotency-Key.
Requête
curl -X DELETE 'https://api.dzbuild.app/v1/promo-codes/20' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: promo-20-delete"
Réponse 200
{
"data": {
"deleted": true,
"id": 20
}
}
Réduction de 100%
Un code percentage à 100 rend les produits gratuits pour tout acheteur qui a le code. Une création, ou une modification qui envoie discount_type ou discount_value, n'est pas écrite au premier appel quand le résultat est un code percentage à 100% :
- L'appel répond
422 confirmation_requiredet n'écrit rien. L'erreur porteconfirm_token(usage unique, valable600secondes),actionetwill_change, un résumé à montrer au marchand. - Une fois que le marchand approuve, renvoyez le même corps avec
confirm_tokenen plus et une nouvelleIdempotency-Key(la première clé est liée au corps sans le jeton). Un jeton expiré, déjà utilisé ou qui ne correspond plus à la requête répond422 confirmation_staleavec un nouveau jeton.
Avec une clé créée depuis le dashboard, vous pouvez éviter cet aller-retour en envoyant confirm_full_discount: true dès le premier appel. Le DZBuild Copilot ne peut pas utiliser ce champ et passe toujours par le jeton.
{
"error": {
"code": "confirmation_required",
"message": "A 100% discount makes every order free ...",
"confirm_token": "cft_xxxxxxxxxxxxxxxx",
"confirm_token_expires_in": 600,
"action": "promo.full_discount:new",
"will_change": {
"action": "Create a promo code that makes orders free",
"code": "FREEGIFT",
"discount": "100% off the whole order subtotal",
"reversible": true,
"note": "Any customer with this code pays 0 for the goods. Orders already placed with it cannot be reversed by deleting the code."
}
}
}
Sur une modification, action se termine par l'id du code au lieu de new, et will_change.action devient Change promo code #20 to make orders free.
curl -X POST 'https://api.dzbuild.app/v1/promo-codes' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: promo-freegift-approved" \
-d '{"code": "FREEGIFT", "discount_type": "percentage", "discount_value": 100, "max_uses": 1, "confirm_token": "cft_REPLACE_WITH_TOKEN"}'
Historique des changements et annulation
Chaque création, modification et suppression faite par l'API est enregistrée. Les codes modifiés sur la page du dashboard ne sont pas enregistrés, on ne peut donc pas les annuler par l'API.
GET /v1/changes?entity=promo_codeliste les changements des codes promo, du plus récent au plus ancien, avecstore:read. Chaque élément porte l'iddu changement, l'id du code dansentity_id, l'action(create,updateoudelete) et unsummary.POST /v1/changes/{id}/undodemandepromos:write, uneIdempotency-Keyet l'add-on activé, comme toute écriture.- Annuler une modification remet les valeurs précédentes, sans confirmation, même une réduction de 100% ou une date d'expiration déjà passée. Si le code a été supprimé depuis, l'annulation répond
422 restore_target_missing. - Annuler une suppression recrée le code avec son nombre d'utilisations, et garde son id quand cet id est encore libre.
- Annuler une création répond
422 nothing_to_restore: supprimez plutôt le code. - Le jeton d'une application installée ne peut ni lire ni annuler les changements : les deux répondent
403 forbidden.
curl -X POST 'https://api.dzbuild.app/v1/changes/500/undo' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: undo-500"
{
"data": {
"undone": true,
"change_id": 500,
"entity": "promo_code",
"undo_change_id": 510
}
}
Un changement annulé une seconde fois répond 409 already_undone.
Erreurs
| HTTP | Code | Cause |
|---|---|---|
| 400 | bad_request | L'id dans le chemin n'est pas composé de chiffres, le corps n'est pas un JSON valide, ou Idempotency-Key manque ou est mal formé. |
| 403 | forbidden | Missing scope: promos:read ou Missing scope: promos:write, ou API access requires an active Enterprise plan pour une clé du marchand dont la boutique n'a pas de plan Enterprise actif. |
| 404 | not_found | Aucun code promo avec cet id dans la boutique. |
| 409 | addon_inactive | L'add-on Codes promotionnels n'est pas activé sur la boutique. Écritures seulement. |
| 409 | code_exists | Un autre code de la boutique a déjà ce texte. |
| 422 | invalid_code | code fait moins de 2 ou plus de 30 caractères, ou contient un caractère autre que A-Z, 0-9, - et _. |
| 422 | invalid_discount_type | discount_type manque, ou n'est ni percentage ni fixed. |
| 422 | invalid_discount_value | discount_value manque, n'est pas un nombre, vaut 0 ou moins, ou dépasse 100 pour percentage. |
| 422 | invalid_min_order_amount | min_order_amount n'est pas un nombre. |
| 422 | invalid_max_uses | max_uses n'est pas un nombre, ou est inférieur à 1. |
| 422 | invalid_starts_at, invalid_expires_at | La date ne peut pas être lue. |
| 422 | expires_at_in_past | Le expires_at envoyé n'est pas dans le futur. |
| 422 | invalid_date_window | expires_at n'est pas après starts_at. |
| 422 | confirmation_required, confirmation_stale | Une réduction de 100% attend l'accord du marchand. Voir la section plus haut. |
| 422 | idempotency_key_reuse | La même Idempotency-Key a servi avec un autre corps ou un autre chemin. |
| 500 | server_error | L'écriture a échoué. Réessayez avec la même Idempotency-Key. |
Une réponse 4xx est conservée avec son Idempotency-Key pendant 24 heures et rejouée à tout réessai avec le même corps. Après avoir activé l'add-on ou corrigé le corps, envoyez l'appel avec une nouvelle clé. Les erreurs communes à tous les endpoints, comme 401, 402 et 429, sont dans Erreurs, et les règles de réessai dans Idempotence.
Limites connues
- Les utilisations peuvent dépasser
max_uses. Quand plusieurs acheteurs commandent avec le même code au même moment,used_countpeut finir au-dessus demax_uses. - Pas de webhook. Créer, modifier ou supprimer un code promo n'envoie aucun événement webhook.