Aller au contenu principal

Produits

Le produit est l'unité vendable de base d'une boutique. Tous les appels produit sont scopés à la boutique de la clé appelante — vous ne pouvez jamais toucher accidentellement les données d'un autre marchand.

GET /v1/products

Liste les produits. Pagination par curseur. Mis en cache en périphérie pendant 30 s.

Auth : clé plateforme avec products:read.

Paramètres de requête

ParamTypeDéfautNotes
limitint (1–200)50Taille de page
cursorstringDu next_cursor d'une réponse précédente
statusactive | draft | archivedFiltre par statut
searchstringMatch sur name (LIKE) et sku exact

Requête

curl 'https://api.dzbuild.app/v1/products?limit=10&status=active' \
-H "Authorization: Bearer $DZ_KEY"

Réponse 200

{
"data": {
"items": [
{
"id": 26,
"name": "PRO",
"slug": "pro",
"short_description": null,
"price": 1000,
"compare_price": null,
"sku": "",
"stock_quantity": 0,
"track_stock": false,
"status": "active",
"has_variants": true,
"featured": false,
"primary_image": "13_1768313552_b33d660c_1562f6687591.webp",
"created_at": "2026-01-13 15:06:06",
"updated_at": "2026-01-13 15:12:32"
}
],
"next_cursor": null,
"has_more": false
},
"meta": { "request_id": "...", "api_version": "v1" }
}

primary_image est un nom de fichier. Construisez l'URL avec https://cdn.dzbuild.app/<store_id>/products/<filename> (ou votre domaine CDN personnalisé). Le chemin complet est dans images[].url sur GET /v1/products/{id}.

GET /v1/products/{id}

Détail complet du produit incluant images et variantes.

Auth : clé plateforme avec products:read.

Requête

curl https://api.dzbuild.app/v1/products/26 \
-H "Authorization: Bearer $DZ_KEY"

Réponse 200

{
"data": {
"id": 26,
"name": "PRO",
"slug": "pro",
"description": "- Single store\n- Up to 300 products\n- ...",
"short_description": null,
"category_id": null,
"pricing": {
"price": 1000,
"compare_price": null,
"cost_price": null
},
"inventory": {
"sku": "",
"barcode": null,
"track_stock": false,
"stock_quantity": 0,
"low_stock_alert": 5
},
"shipping": {
"weight": null, "height": null, "width": null, "length": null,
"do_insurance": false
},
"status": "active",
"featured": false,
"has_variants": true,
"images": [
{ "id": 28, "url": "13_1768313552_b33d660c_1562f6687591.webp",
"is_primary": true, "sort_order": 0 }
],
"variants": [
{
"id": 11,
"name": "Duration",
"type": "text",
"options": [
{ "id": 14, "value": "30 days", "color_code": null, "image_id": null, "stock": null },
{ "id": 15, "value": "90 days", "color_code": null, "image_id": null, "stock": null },
{ "id": 16, "value": "180 days", "color_code": null, "image_id": null, "stock": null },
{ "id": 17, "value": "365 days", "color_code": null, "image_id": null, "stock": null }
]
}
],
"created_at": "2026-01-13 15:06:06",
"updated_at": "2026-01-13 15:12:32"
}
}

POST /v1/products — créer

Auth : clé plateforme avec products:write. Nécessite Idempotency-Key.

Corps

ChampTypeRequisNotes
namestring (1–255)
pricenumber ≥ 0DZD
compare_pricenumber ≥ 0 | nullPrix barré
cost_pricenumber ≥ 0 | nullInterne — jamais montré au client
descriptionstringLong format, retours à la ligne acceptés
short_descriptionstring ≤ 500Phrase courte
skustring ≤ 100SKU interne
barcodestring ≤ 100UPC/EAN
weightnumberkg, pour la livraison
shipping_height / width / lengthnumbercm
do_insuranceboolForcer l'assurance livraison sur ce produit
track_stockboolDéfaut false
stock_quantityint ≥ 0Si track_stock
low_stock_alertint ≥ 0Défaut 5. Déclenche le webhook product.stock_low.
variant_stock_enabledboolSKUs par variante
combination_stock_enabledboolSKUs par (couleur × taille). Implique variant_stock_enabled.
category_idintDoit exister dans votre boutique
statusactive | draft | archivedDéfaut draft
featuredboolDéfaut false

Quand variant_stock_enabled ou combination_stock_enabled vaut true, track_stock est désactivé automatiquement (les variantes gèrent leur propre stock).

Limite par plan

Free : 5 produits actifs. Pro : 300. Unlimited / Enterprise : illimité. Le check ne compte que les lignes status = 'active'. Les brouillons ne comptent pas. À la limite :

{ "error": { "code": "bad_request",
"message": "Plan 'free' allows at most 5 active products. Upgrade to add more." } }

Requête

curl -X POST 'https://api.dzbuild.app/v1/products' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"name": "T-shirt - Cotton 200gsm",
"price": 1500,
"compare_price": 1900,
"description": "100% cotton, made in Algeria.",
"sku": "TS-COT-200",
"stock_quantity": 50,
"track_stock": true,
"status": "draft"
}'

Réponse 201

Renvoie la même forme que GET /v1/products/{id}. id, slug et created_at sont maintenant remplis. Le slug est généré automatiquement depuis name et rendu unique dans votre boutique. Vous pouvez le forcer en envoyant votre propre slug (sujet à normalisation [a-z0-9-]).

Erreurs

CodeCause
bad_request "Body must be valid JSON"Content-Type incorrect ou JSON malformé
bad_request "name is required (1-255 chars)"Nom manquant ou trop long
bad_request "price must be a non-negative number"Prix invalide
bad_request "category_id N does not belong to this store"ID de catégorie cross-store
bad_request "Plan 'free' allows at most …"Limite de plan

PATCH /v1/products/{id} — mettre à jour

Auth : clé plateforme avec products:write. Nécessite Idempotency-Key.

Mise à jour partielle — n'envoyez que les champs à changer. Les champs non spécifiés sont préservés.

curl -X PATCH 'https://api.dzbuild.app/v1/products/26' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "price": 1200, "status": "active" }'

Renvoie 200 et le produit complet mis à jour. Si le produit n'existe pas (ou appartient à une autre boutique), vous obtenez 404 not_found.

Renommer via PATCH { name: ... } régénère automatiquement le slug uniquement si vous n'avez pas envoyé slug explicitement. Envoyez slug pour préserver une URL spécifique après un renommage.

DELETE /v1/products/{id}

Auth : clé plateforme avec products:write. Nécessite Idempotency-Key.

curl -X DELETE 'https://api.dzbuild.app/v1/products/26' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: del-26-2026-04-30"

Réponse :

{ "data": { "deleted": true, "id": 26 } }

C'est une suppression dure — la ligne produit est supprimée et les FK en cascade nettoient images, variantes, offres et combinaisons. Les fichiers images dans R2 sont laissés à un sweep de nettoyage asynchrone (on ne bloque pas l'appel API sur la suppression R2).

POST /v1/products/{id}/images (à venir en v1.1)

Retournera une URL PUT pré-signée R2 pour que votre client uploade les fichiers directement vers Cloudflare R2 — pas d'aller-retour vers l'origine algérienne, pas de blocage FPM par le traitement d'image.

D'ici là, les uploads d'images passent par le tableau de bord : Produits → Modifier → Images.