Passer au contenu principal

Landing pages

CRUD pour les landing pages — pages de conversion mono-produit avec sections (carrousels, formulaires de commande, faux visiteurs, comptes à rebours, offres spéciales).

Écrit par Support

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

limit

int 1–200

Défaut 50

cursor

string

Opaque

status

active | draft

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

status

Strictement active ou draft — il n'y a pas d'état archived pour les landing pages.

language

ar, fr ou en.

public_url

Adresse en ligne de la page : l'adresse de la boutique (son domaine personnalisé une fois actif, sinon son sous-domaine), puis /landing/ et le slug. null tant que la boutique n'a pas d'adresse. Utilisez-la telle quelle plutôt que de construire l'URL vous-même.

product_id

Le produit lié, ou null. Une section qui prend des commandes (order_form, order_button, product_offers) a besoin de lui ou de son propre settings.product_id. Une fois défini, PATCH peut le changer pour un autre produit, mais pas le remettre à null.

views

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.

is_purchased

true 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_count

Endpoint détail uniquement — un comptage en direct des sections de la page, calculé à chaque requête.

meta_title / meta_description

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

title

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

slug

string

Auto-déduit de title si omis. Un slug que vous fournissez ici est stocké sans normalisation — envoyez-en un propre

status

active | draft

Défaut draft. Toute autre valeur est silencieusement ramenée à draft

language

ar | fr | en

Défaut ar. Toute autre valeur est silencieusement ramenée à ar

product_id

int

Doit appartenir à votre boutique ; la page lie ce produit

meta_title

string ≤ 255

Titre 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_description

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

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

limit_reached (403)

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

GET /v1/landing-page-section-types

200 : section_types, chacun un type avec ses default_settings

GET /v1/landing-pages/{id}/sections

200 : landing_page_id et sections dans l'ordre d'affichage (jusqu'à 500, sans pagination)

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

section_type (ou type), settings facultatif

201 : la nouvelle section, ajoutée en bas de la page

POST /v1/landing-pages/{id}/sections/batch

sections : jusqu'à 30 objets avec type (ou section_type) et settings facultatif

201 : les nouvelles sections. Tout ou rien : une seule entrée invalide et rien n'est enregistré

POST /v1/landing-pages/{id}/sections/reorder

sections : tous les ids de sections de la page, une fois chacun, dans le nouvel ordre

200 : les sections dans leur nouvel ordre

PATCH /v1/landing-pages/{id}/sections/{section_id}

settings, replace facultatif

200 : la section mise à jour

DELETE /v1/landing-pages/{id}/sections/{section_id}

200 : { "deleted": true, "id": 901, "landing_page_id": 42 }

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": true pour repartir des valeurs par défaut du type. Les objets imbriqués fusionnent clé par clé ; les listes comme slides, items, offers et fields sont remplacées en entier. Le type d'une section ne peut pas changer.

  • Une section order_form, order_button ou product_offers a besoin d'un produit : le product_id de la page ou son propre settings.product_id. Sans produit, l'appel répond 422 landing_page_has_no_product. Un settings.product_id d'une autre boutique répond 422 validation_error.

  • Les champs du formulaire de commande show_name, show_phone et show_wilaya sont toujours enregistrés à true dans toute section qui les porte.

  • Une liste slides accepte au plus 20 entrées et une liste items au 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épond 422 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épondent 404 not_found), et une section absente de la page répond 404 section_not_found. Une liste de reorder qui répète ou oublie une section répond 422 validation_error.

  • Les changements de sections apparaissent dans GET /v1/changes. Une modification, une suppression ou un reorder s'annule avec POST /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

empty_page

blocking

La page n'a aucune section, elle s'affiche vide

order_form_without_product

blocking

Une section prend des commandes, mais ni la page ni la section ne désigne un produit : les commandes seraient enregistrées à 0 DA

section_product_not_found

blocking

Une section pointe vers un produit qui n'est pas dans votre boutique

no_order_form

warning

Rien sur la page ne peut prendre une commande

multiple_order_forms

warning

Plus d'un order_form ou order_button s'affiche

variants_need_page_product

warning

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 product_id sur la page

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

title

string

✅

Au moins 3 caractères, coupé à 255

product_id

int

✅

Un produit actif de votre boutique avec au moins une image

description

string

Brief pour la page, coupé à 2000 caractères

language

ar | fr | en

Défaut ar

size

medium | tall

Défaut medium. medium coûte 20 crédits IA, tall en coûte 45

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 } }.

Avez-vous trouvé la réponse à votre question ?