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, formulaires de commande, faux visiteurs, comptes à rebours, etc.) se construisent par l'API avec les endpoints de sections ci-dessous, ou dans le tableau de bord.
Limites par plan
Plan | Landing pages (tous statuts — les brouillons comptent) |
Free | 0 (achat unique : 1000 DZD / à vie chacune) |
Pro | 3 |
Unlimited / Enterprise | illimité |
Le plafond s'applique sur toutes les voies de création, API comprise, et compte chaque landing page, brouillons inclus. Sur une boutique à sa limite, POST /v1/landing-pages (et POST /v1/landing-pages/generate) répond 403 limit_reached. Une boutique sur le plan Free ne peut créer une page que tant qu'il lui reste un achat de landing page non utilisé ; POST /v1/landing-pages marque cette page is_purchased: true, et c'est ce qui la rend visible sur la vitrine.
GET /v1/landing-pages
Liste les landing pages. Pagination par curseur. Servi à neuf à chaque appel, comme GET /v1/landing-pages/{id}.
Auth : clé plateforme avec landing_pages:read, que GET /v1/landing-pages/{id} demande aussi. Une clé sans ce scope reçoit 403 forbidden.
Paramètres de requête
Param | Type | Notes |
| int 1–200 | Défaut 50 |
| string | Opaque |
|
| Filtre |
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",
"public_url": "https://your-store.example.com/landing/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",
"public_url": "https://your-store.example.com/landing/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
Champ | Notes |
| Strictement |
|
|
| Adresse en ligne de la page : l'adresse de la boutique (son domaine personnalisé une fois actif, sinon son sous-domaine), puis |
| Le produit lié, ou |
| Lecture 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. |
|
|
| Endpoint détail uniquement — un comptage en direct des sections de la page, calculé à chaque requête. |
| Balises SEO. Voir la note sous création. |
POST /v1/landing-pages — créer
Auth : clé plateforme avec landing_pages:write et landing_pages:read. La réponse relit la page : avec landing_pages:write seul, la page est enregistrée et l'appel répond 403 forbidden ; il en va de même pour PATCH et /publish. Nécessite Idempotency-Key.
Corps
Champ | Type | Requis | Notes |
| string, 1 à 255 octets | ✅ | La limite compte les octets, pas les lettres : une lettre arabe prend 2 octets, un titre arabe plafonne donc vers 127 lettres |
| string | Auto-déduit de | |
|
| Défaut | |
|
| Défaut | |
| int | Doit appartenir à votre boutique ; la page lie ce produit | |
| string ≤ 255 | Titre SEO. Omis via l'API, il est stocké et renvoyé comme | |
| string | Description 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
Code | Cause |
| Content-Type incorrect ou JSON malformé |
| Titre manquant ou trop long |
| ID cross-boutique |
| La boutique a atteint la limite de landing pages de son plan (brouillons compris). Sur Free : plus aucun achat de landing page non utilisé |
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 : ajoutez-les avec les endpoints de sections ci-dessous. Le contrôle de publication ne tourne pas à la création : créez la page en draft et publiez-la une fois ses sections en place ; une page créée avec status: active est en ligne vide.
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 octets. Un slug envoyé sur PATCH est normalisé, contrairement à la création. Passer status à active lance le contrôle de publication (voir /publish plus bas). Une fois qu'une page a un produit, product_id: null répond 422 product_required ; envoyez un autre id de produit pour changer de produit.
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. Quand le slug change, par un renommage ou explicitement, l'ancienne adresse continue de fonctionner et redirige vers la nouvelle.
Sections
Une page affiche ses sections de haut en bas. Les lectures de sections demandent landing_pages:read, les écritures landing_pages:write, et chaque écriture demande une Idempotency-Key.
Endpoint | Corps | Réponse |
|
| |
|
| |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Une section porte id, section_type, sort_order et settings. Les réponses qui relisent la page (la liste, le reorder et PATCH) portent aussi created_at et updated_at. Les 14 types sont image, order_form, order_button, free_text, contact_button, countdown, fake_visitors, special_offer, price_display, product_offers, custom_form, image_carousel, announcement_bar et testimonials. Lisez GET /v1/landing-page-section-types avant d'écrire, pour que vos clés de réglages correspondent à ce que la page affiche.
Les réglages envoyés sont fusionnés avec les valeurs par défaut du type : un seul appel suffit pour créer une section entièrement réglée. Sur PATCH, ils sont fusionnés avec ceux enregistrés ; envoyez
"replace": truepour repartir des valeurs par défaut du type. Les objets imbriqués fusionnent clé par clé ; les listes commeslides,items,offersetfieldssont remplacées en entier. Le type d'une section ne peut pas changer.Une section
order_form,order_buttonouproduct_offersa besoin d'un produit : leproduct_idde la page ou son propresettings.product_id. Sans produit, l'appel répond422 landing_page_has_no_product. Unsettings.product_idd'une autre boutique répond422 validation_error.Les champs du formulaire de commande
show_name,show_phoneetshow_wilayasont toujours enregistrés àtruedans toute section qui les porte.Une liste
slidesaccepte au plus 20 entrées et une listeitemsau plus 30 (422 too_many_items). Les réglages encodés ne peuvent pas dépasser 262144 octets (422 settings_too_large). Un type inconnu répond422 invalid_section_type.Sur une écriture, une page hors de votre boutique répond
404 landing_page_not_found(la liste et le contrôle répondent404 not_found), et une section absente de la page répond404 section_not_found. Une liste de reorder qui répète ou oublie une section répond422 validation_error.Les changements de sections apparaissent dans
GET /v1/changes. Une modification, une suppression ou un reorder s'annule avecPOST /v1/changes/{id}/undo, et une section supprimée qui revient par une annulation reçoit un nouvel id. L'ajout d'une section ne s'annule pas : supprimez-la plutôt.
curl -X POST 'https://api.dzbuild.app/v1/landing-pages/42/sections/batch' \
-H "Authorization: Bearer $DZ_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: $(uuidgen)" \
-d '{
"sections": [
{ "type": "announcement_bar" },
{ "type": "order_form" }
]
}'
GET /v1/landing-pages/{id}/check
Signale ce qu'un acheteur trouverait cassé sur la page. Auth : landing_pages:read.
La réponse porte landing_page_id, status, product_id, sections (le nombre de sections contrôlées), publishable, blocked_by (le premier message bloquant, ou null) et problems. Chaque problème a code, severity (blocking ou warning) et message ; les problèmes liés à une section portent aussi section_id.
Code | Gravité | Sens |
|
| La page n'a aucune section, elle s'affiche vide |
|
| Une section prend des commandes, mais ni la page ni la section ne désigne un produit : les commandes seraient enregistrées à 0 DA |
|
| Une section pointe vers un produit qui n'est pas dans votre boutique |
|
| Rien sur la page ne peut prendre une commande |
|
| Plus d'un |
|
| Une section désigne un produit à variantes alors que la page n'a pas de produit. Les sélecteurs de variantes ne s'affichent qu'à partir du produit de la page : définissez |
La publication est refusée tant qu'un problème bloquant subsiste (voir plus bas).
POST /v1/landing-pages/{id}/publish
Raccourci : passer le status à active. Équivaut à PATCH ... { status: "active" }, et refusé de la même façon : tant que GET /v1/landing-pages/{id}/check signale un problème bloquant, l'appel répond 422 page_not_publishable et l'erreur porte la liste problems. Les modifications qui n'envoient pas status ne passent pas par ce contrôle, même sur une page en ligne.
curl -X POST 'https://api.dzbuild.app/v1/landing-pages/42/publish' \ -H "Authorization: Bearer $DZ_KEY" \ -H "Idempotency-Key: publish-42-$(date +%s)"
POST /v1/landing-pages/generate
Construit une landing page à partir d'un de vos produits avec l'IA. Répond 202 tout de suite avec une tâche à suivre ; une génération prend environ deux minutes. Auth : le scope ai:generate. Les clés créées dans le tableau de bord ou avec POST /v1/keys et les applications tierces ne le portent pas et reçoivent 403 forbidden ; les connexions Claude, ChatGPT et DZBuild Copilot le portent. Nécessite Idempotency-Key.
Champ | Type | Requis | Notes |
| string | ✅ | Au moins 3 caractères, coupé à 255 |
| int | ✅ | Un produit actif de votre boutique avec au moins une image |
| string | Brief pour la page, coupé à 2000 caractères | |
|
| Défaut | |
|
| Défaut |
La réponse porte task_id, size, credits_charged, eta_seconds et poll. Les crédits sont débités au lancement et rendus si la génération échoue ou dépasse le délai. Erreurs : 400 bad_request (titre manquant ou de moins de 3 caractères), 402 quota_exceeded (crédits IA insuffisants), 403 limit_reached (limite de landing pages du plan, avec limit et current), 409 already_processing (une seule génération à la fois par boutique), 422 product_required, product_not_found ou product_has_no_image, 429 rate_limited ou too_many_concurrent, 503 provider_unavailable (aucun crédit débité).
Suivez GET /v1/landing-pages/generate/{task_id} avec landing_pages:read. La réponse porte task_id, status (processing pendant la construction, puis completed ou failed), landing_page_id dès que la page existe, current_step et error. Une génération encore en cours après 10 minutes passe à failed avec l'erreur timeout à la consultation suivante, et ses crédits sont rendus.
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 } }.