Aller au contenu principal

Landing pages

Une landing page est une page de conversion focalisée sur un seul produit. Elles sont indépendantes du catalogue boutique — vous pouvez avoir une landing page sans produit en ligne (pour des lancements à venir), ou une liée à un produit pour des pubs payantes.

Les sections (carrousels, faux visiteurs, comptes à rebours, etc.) sont gérées dans le tableau de bord en v1 ; l'API ne fait que CRUD sur l'enregistrement parent. Une mise à jour v1.1 exposera aussi le CRUD des sections.

Limites par plan

PlanLanding pages (tous statuts — les brouillons comptent)
Free0 (achat unique : 1000 DZD / à vie chacune)
Pro3
Unlimited / Enterpriseillimité

Le plafond n'est appliqué que par les flux création / duplication du tableau de bord, en comptant chaque landing page, brouillons inclus. L'API n'applique rien : POST /v1/landing-pages suivi de /publish contourne totalement le plafond, et sur un plan payant les pages en trop s'affichent bien en ligne sur la vitrine. Sur le plan Free, les pages restent invisibles sauf si la page a été achetée (is_purchased).

GET /v1/landing-pages

Liste les landing pages. Pagination par curseur. Mis en cache pendant 30 s — vérifiez l'en-tête de réponse X-Cache: HIT|MISS. GET /v1/landing-pages/{id} n'est pas mis en cache.

Auth : n'importe quelle clé plateforme active de la boutique (landing_pages:read n'est pas appliqué en v1 ; seul landing_pages:write est contrôlé, sur les endpoints d'écriture).

Paramètres de requête

ParamTypeNotes
limitint 1–200Défaut 50
cursorstringOpaque
statusactive | draftFiltre

Un status non reconnu est ignoré : toutes les pages sont renvoyées plutôt qu'un 400.

Réponse 200

{
"data": {
"items": [
{
"id": 42,
"title": "Black T-Shirt — 30% off",
"slug": "black-tshirt-30-off",
"status": "active",
"language": "ar",
"product_id": 26,
"views": 1543,
"is_purchased": false,
"created_at": "2026-03-01 10:00:00",
"updated_at": "2026-03-15 14:22:11"
}
],
"next_cursor": null,
"has_more": false
}
}

GET /v1/landing-pages/{id}

Détail avec section_count.

{
"data": {
"id": 42,
"title": "Black T-Shirt — 30% off",
"slug": "black-tshirt-30-off",
"status": "active",
"language": "ar",
"product_id": 26,
"views": 1543,
"is_purchased": false,
"meta_title": "Black T-Shirt — Cotton 200gsm — 30% off | DZBuild",
"meta_description": "Limited-time offer on our cotton black t-shirt.",
"section_count": 7,
"created_at": "2026-03-01 10:00:00",
"updated_at": "2026-03-15 14:22:11"
}
}

Référence des champs

ChampNotes
statusStrictement active ou draft — il n'y a pas d'état archived pour les landing pages.
languagear, fr ou en.
product_idLe produit lié, ou null. Une page sans product id ne peut pas tarifer correctement son formulaire de commande.
viewsLecture seule. Compté à chaque consultation de la page publique ; l'API ne peut pas l'écrire et il n'existe aucun moyen de le remettre à zéro.
is_purchasedtrue une fois la page achetée définitivement (1000 DZD / à vie). Sur le plan Free, c'est ce qui rend la page visible sur la vitrine.
section_countEndpoint détail uniquement — un comptage en direct des sections de la page, calculé à chaque requête.
meta_title / meta_descriptionBalises SEO. Voir la note sous création.

POST /v1/landing-pages — créer

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

Corps

ChampTypeRequisNotes
titlestring 1–255
slugstringAuto-déduit de title si omis. Un slug que vous fournissez ici est stocké sans normalisation — envoyez-en un propre
statusactive | draftDéfaut draft. Toute autre valeur est silencieusement ramenée à draft
languagear | fr | enDéfaut ar. Toute autre valeur est silencieusement ramenée à ar
product_idintDoit appartenir à votre boutique ; la page lie ce produit
meta_titlestring ≤ 255Titre SEO. Omis via l'API, il est stocké et renvoyé comme null (contrairement au formulaire du tableau de bord, qui y recopie title). La page publique affiche quand même title en repli, le titre visible est donc correct dans les deux cas
meta_descriptionstringDescription SEO

Les slugs sont rendus uniques dans votre boutique par ajout de -2, -3, … Une base de slug vide retombe sur landing- suivi de 6 caractères hex.

Erreurs

CodeCause
bad_request "Body must be valid JSON"Content-Type incorrect ou JSON malformé
bad_request "title is required (1-255 chars)"Titre manquant ou trop long
bad_request "product_id N does not belong to this store"ID cross-boutique

Requête

curl -X POST 'https://api.dzbuild.app/v1/landing-pages' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"title": "Black T-Shirt — 30% off",
"language": "ar",
"product_id": 26,
"status": "draft"
}'

Renvoie 200 (et non 201) avec la même forme que GET /v1/landing-pages/{id}. La nouvelle landing page n'a aucune section — peuplez-les depuis le dashboard.

PATCH /v1/landing-pages/{id}

Mise à jour partielle.

PATCH valide plus strictement que la création : un status invalide renvoie 400 bad_request (« status must be active or draft ») et un language invalide renvoie 400 (« language must be ar, fr, or en ») au lieu d'être coercé. title doit toujours faire 1 à 255 caractères. Un slug envoyé sur PATCH est normalisé, contrairement à la création.

curl -X PATCH 'https://api.dzbuild.app/v1/landing-pages/42' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{ "title": "Black T-Shirt — Spring promo" }'

Renommer régénère slug automatiquement uniquement si vous n'avez pas envoyé slug explicitement.

POST /v1/landing-pages/{id}/publish

Raccourci : passer le status à active. Équivaut à PATCH ... { status: "active" }.

curl -X POST 'https://api.dzbuild.app/v1/landing-pages/42/publish' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: publish-42-$(date +%s)"

DELETE /v1/landing-pages/{id}

Suppression dure. Les sections de la page sont supprimées avec elle.

curl -X DELETE 'https://api.dzbuild.app/v1/landing-pages/42' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Idempotency-Key: del-42"

Réponse : { "data": { "deleted": true, "id": 42 } }.