Composer une page web et la ranger dans un site
Créer une page web en sections et en blocs, y déposer des images et des vidéos, la publier, la personnaliser pour une fiche, puis la ranger dans un site avec son accueil et son menu.
Une page web est publique : une page d'offre, d'inscription, un site de quelques pages. Ses blocs sont dans Fields : au premier
niveau, des sections ; dans chaque section, ses blocs. Une page naît en brouillon ; la publication en prend un
instantané, seul servi aux visiteurs.
1Créer la page
POST https://staging-api.docage.com/WebPages
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"Name": "Bilan offert",
"Slug": "bilan-offert",
"Fields": [
{
"Type": "section",
"Label": "Accroche",
"RowIndex": 1,
"Fields": [
{ "Type": "header", "Subtype": "h1", "Label": "Votre bilan offert", "TextAlign": "center", "RowIndex": 1 },
{ "Type": "paragraph", "Label": "Prenez rendez-vous avant le 30 novembre.", "Lead": true, "RowIndex": 2 },
{ "Type": "cta", "Label": "Je prends rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "RowIndex": 3 }
]
}
]
}curl -X POST "https://staging-api.docage.com/WebPages" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"Name": "Bilan offert",
"Slug": "bilan-offert",
"Fields": [{"Type": "section", "Label": "Accroche", "RowIndex": 1, "Fields": [{"Type": "header", "Subtype": "h1", "Label": "Votre bilan offert", "TextAlign": "center", "RowIndex": 1}, {"Type": "paragraph", "Label": "Prenez rendez-vous avant le 30 novembre.", "Lead": true, "RowIndex": 2}, {"Type": "cta", "Label": "Je prends rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "RowIndex": 3}]}]
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/WebPages");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"Name": "Bilan offert",
"Slug": "bilan-offert",
"Fields": [
{
"Type": "section",
"Label": "Accroche",
"RowIndex": 1,
"Fields": [
{
"Type": "header",
"Subtype": "h1",
"Label": "Votre bilan offert",
"TextAlign": "center",
"RowIndex": 1
},
{
"Type": "paragraph",
"Label": "Prenez rendez-vous avant le 30 novembre.",
"Lead": true,
"RowIndex": 2
},
{
"Type": "cta",
"Label": "Je prends rendez-vous",
"TargetKind": "url",
"Href": "https://example.com/rdv",
"RowIndex": 3
}
]
}
]
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/WebPages", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"Name": "Bilan offert",
"Slug": "bilan-offert",
"Fields": [{"Type": "section", "Label": "Accroche", "RowIndex": 1, "Fields": [{"Type": "header", "Subtype": "h1", "Label": "Votre bilan offert", "TextAlign": "center", "RowIndex": 1}, {"Type": "paragraph", "Label": "Prenez rendez-vous avant le 30 novembre.", "Lead": true, "RowIndex": 2}, {"Type": "cta", "Label": "Je prends rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "RowIndex": 3}]}]
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse est l'identifiant de la page.
- Une section :
Label, son nom ;AnchorId, la cible d'un lien#…;Background, son fond — un rôle de la palette de la page (Light,Medium,Dark,Darker,Contrast), jamais une couleur — ouBackgroundImageId, une image de la page (§ 4). - La mise en page : dans une section,
RowIndexnumérote les lignes à partir de1. Deux blocs du mêmeRowIndexsont côte à côte, dans l'ordre deColumnIndex, chacun large deColumnWidthdouzièmes (la ligne entière d'office). Donnez unRowIndexà chaque bloc. - Les textes (
Label,Title,Text…) sont du texte brut, jamais du HTML ; une ligne vide sépare deux paragraphes.
Type |
Bloc | Ses réglages |
|---|---|---|
header |
Un titre | Label, Subtype (h1, h2, h3), TextAlign (left, center, right) |
paragraph |
Un texte | Label, Lead (un texte d'introduction, plus grand), TextAlign |
cta |
Un bouton | Label, TargetKind (url, page, anchor, email, phone), Href ou TargetPageId, Variant (primary, secondary), NewTab, TextAlign, ComponentName |
cards |
Des cartes | Columns (1 à 4), Items : Title, Text, Icon ou ImageId (§ 4), LinkLabel, LinkHref |
form-embed |
Un formulaire | FormId (un modèle de formulaire web de l'organisation), Display (inline ou popup), OpenLabel |
navigation |
Un menu | BrandText, Links (Label, TargetKind, Href ou TargetPageId), ou Source à site pour le menu du site |
image |
Une image déposée | ImageId et ImageName (§ 4), Alt (son texte pour qui ne la voit pas), ImageAlign, WidthPercent, Href |
video |
Une vidéo | En ligne : VideoSource à link, VideoUrl (YouTube, Vimeo ou Dailymotion). Déposée : VideoSource à upload, VideoId et VideoName, PosterImageId (§ 4) |
social |
Des liens sociaux | Links : Network (linkedin, facebook, instagram, x-twitter, youtube, tiktok, github, website) et Url, en https:// |
html |
Un fragment HTML | Code, assaini : ses scripts ne s'exécutent pas |
brand-logo |
Le logo de la marque | Size (small, medium, large), TextAlign |
divider |
Un séparateur | — |
spacer |
Un espace | Height, de 8 à 240 pixels |
ComponentName d'un bouton est le nom sous lequel ses clics sont suivis. Une page ne recueille pas de réponses elle-même : elle
embarque un formulaire.
2Relire, puis publier
GET https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/State
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/State" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Get, "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/State");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/State", {
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse dit le statut de la page (Status : 0 brouillon, 1 publiée, 2 dépubliée), son numéro de version publiée
(PublishedVersion, 0 jamais publiée), et son adresse : Slug, celle que le serveur a retenue — normalisée, rendue unique
(« bilan-offert-2 ») —, et Address, son chemin public, vide tant que l'organisation n'a rien publié.
POST https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/Publish
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…curl -X POST "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/Publish" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…"using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/Publish");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/Publish", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`
}
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;{
"Id": "6b0d2f47-0000-4000-8000-000000001201",
"Status": 1,
"PublishedVersion": 1,
"PublishedAtUtc": "2026-11-10T09:30:00Z",
"Slug": "bilan-offert",
"Address": "/p/cabinet-martin/bilan-offert"
}La page est servie à l'adresse de l'application, https://staging-app.docage.com, suivie de Address. La première publication d'une organisation lui donne
son adresse web, tirée de son nom. 422 : un modèle de page (on publie les pages qui en sont tirées), une page vide, une page refusée
par ses contrôles ; 403 : l'utilisateur ne peut pas la modifier.
POST https://staging-api.docage.com/WebPages/{id}/Unpublish la retire ; publiée de nouveau, elle reprend son brouillon du moment.
3La modifier
PUT https://staging-api.docage.com/WebPages/{id}, avec ce qui change : Name, Slug, Description, Fields, ExpectedMergeEntities, Seo (Title,
Description, ImageId, Indexable), WebSiteId, UseSiteStyle.
Fieldsse remplace d'un bloc : envoyez la liste entière. Les images et les vidéos de la page que plus aucun bloc ne cite sont alors supprimées, sauf celles déposées depuis moins d'un jour.- Modifier une page publiée ne change que son brouillon : les visiteurs voient la version publiée jusqu'à la publication suivante.
- Une page refusée par ses contrôles répond
422, sans rien modifier.
4Déposer une image ou une vidéo
Une image ou une vidéo se dépose sur la page — enregistrée d'abord —, puis un bloc la cite par son identifiant.
curl -X POST "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/Images" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-F "File=@bandeau.jpg;type=image/jpeg"{ "Id": "0f3b9a2c-0000-4000-8000-000000001401", "Name": "bandeau.jpg", "ContentType": "image/jpeg", "Width": 2000, "Height": 800 }- Le formulaire porte un seul fichier (
400sinon). Une image : PNG, JPEG, GIF, WebP ou SVG, de 5 Mo au plus, reconnue à son contenu et jamais à son nom. Une image PNG, JPEG ou WebP est réencodée : ses métadonnées sont retirées — dont la position d'une photo prise au téléphone — et son plus grand côté est plafonné à 2 000 pixels ; un GIF est gardé tel quel, un SVG assaini. - Une vidéo :
POST https://staging-api.docage.com/WebPages/{id}/Videos, de même. MP4 ou WebM, de 50 Mo au plus, gardée telle quelle ; une vidéo de téléphone (.mov) s'exporte d'abord en MP4. Une vidéo plus lourde se publie en ligne et s'intègre par son adresse. - Le fichier appartient à la page, ou au modèle de page, dont les pages tirées l'affichent aussi.
- Les refus :
403vous voyez la page sans pouvoir la modifier ;404la page est introuvable, à la corbeille, ou vous ne la voyez pas ;413la requête dépasse 6 Mo (51 Mo pour une vidéo) ;422un fichier illisible, trop lourd ou d'un autre format.
Recopiez ensuite l'identifiant dans ce qui affiche le fichier, puis enregistrez la page par PUT https://staging-api.docage.com/WebPages/{id}, Fields
entier :
| Ce qui l'affiche | Ce qui cite le fichier |
|---|---|
Un bloc image |
ImageId, avec son nom, ImageName |
Un bloc video déposé (VideoSource à upload) |
VideoId et VideoName ; son image d'aperçu, une image de la page, PosterImageId |
Une carte d'un bloc cards |
ImageId |
| Une section | BackgroundImageId, son image de fond |
Le référencement (Seo) |
ImageId, l'image des aperçus de liens |
{ "Type": "image", "ImageId": "0f3b9a2c-0000-4000-8000-000000001401", "ImageName": "bandeau.jpg", "Alt": "L'équipe du cabinet", "RowIndex": 1 }Un bloc ne cite qu'un fichier de la page ou de son modèle : un autre identifiant est refusé (422). Un fichier que plus rien ne
cite — ni le brouillon, ni la version publiée, ni le référencement — est supprimé au premier enregistrement des blocs qui suit le jour
de son dépôt.
Lire un fichier. GET https://staging-api.docage.com/WebPages/Files/{fileId} rend son contenu, brouillon compris, à qui voit la page — 404 sinon, comme
pour un fichier inconnu. Son type est relu dans le fichier. Une vidéo se lit par plages : l'en-tête Range rend la partie demandée
(206). Les visiteurs, eux, lisent les fichiers de la version publiée à l'adresse de la page.
L'aperçu d'un brouillon, tel que le montre le concepteur, se voit dans l'application, sur la fiche de la page : il n'a pas de route publique.
5La personnaliser
Un texte cite des champs {{…}} : ceux du contexte ({{Organization_Name}}, {{WebPage_Name}}, {{WebSite_Name}}), de la visite,
d'une fiche désignée par son identifiant, ou d'un type de fiche que la page attend — "ExpectedMergeEntities": ["Contact"] pour
{{Contact_FirstName}}. Un champ qui lit une fiche que la page n'attend pas est refusé (422, qui le nomme).
Le lien personnalisé ouvre la page publiée comme la personne à qui il est destiné, et ses champs se remplissent :
POST https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/VisitorLink
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{ "TypeName": "Contact", "EntityId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f" }curl -X POST "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/VisitorLink" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"TypeName": "Contact",
"EntityId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Post, "https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/VisitorLink");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"TypeName": "Contact",
"EntityId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/WebPages/6b0d2f47-0000-4000-8000-000000001201/VisitorLink", {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"TypeName": "Contact",
"EntityId": "9b2d7a46-3c1e-4f0a-8b5d-1a2b3c4d5e6f"
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;La réponse porte Address, le chemin de la page suivi d'un jeton signé (?dv=…), à la suite de https://staging-app.docage.com, et ExpiresAtUtc, sa fin
de validité. TypeName : Contact, DocageUser, MailRecipient, Campaign ou TransactionMember, une fiche de l'organisation de
la page que l'utilisateur voit. Sans lien personnalisé, un champ qui lit une fiche reste vide.
Un modèle d'email cite une page par le jeton {{Link_WebPage[identifiant de la page]}} : à l'envoi, il devient l'adresse de la page,
personnalisée quand l'email n'a qu'un destinataire.
6Ranger des pages dans un site
Le site et ses pages
Créez le site — POST https://staging-api.docage.com/WebSites, avec { "Name": "Cabinet Martin", "Slug": "cabinet" } —, puis rangez-y chaque page :
PUT https://staging-api.docage.com/WebPages/{id}, avec { "WebSiteId": "…" }. Le site est de l'organisation de la page ; une page se range dans un site, un
modèle de page dans un modèle de site. Une page créée suit d'office le style de son site (UseSiteStyle). Une page d'un site est
servie à /p/{organisation}/{site}/{page}.
L'accueil et le menu
PUT https://staging-api.docage.com/WebSites/c3e8a915-0000-4000-8000-000000001301
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…
Content-Type: application/json
{
"HomePageId": "6b0d2f47-0000-4000-8000-000000001201",
"Navigation": [
{ "Label": "Nos offres", "TargetKind": "page", "TargetPageId": "6b0d2f47-0000-4000-8000-000000001202" },
{ "Label": "Prendre rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "Highlighted": true }
]
}curl -X PUT "https://staging-api.docage.com/WebSites/c3e8a915-0000-4000-8000-000000001301" \
-H "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9…" \
-H "Content-Type: application/json" \
-d '{
"HomePageId": "6b0d2f47-0000-4000-8000-000000001201",
"Navigation": [{"Label": "Nos offres", "TargetKind": "page", "TargetPageId": "6b0d2f47-0000-4000-8000-000000001202"}, {"Label": "Prendre rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "Highlighted": true}]
}'using HttpClient client = new();
using HttpRequestMessage request = new(HttpMethod.Put, "https://staging-api.docage.com/WebSites/c3e8a915-0000-4000-8000-000000001301");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
request.Content = new StringContent("""
{
"HomePageId": "6b0d2f47-0000-4000-8000-000000001201",
"Navigation": [
{
"Label": "Nos offres",
"TargetKind": "page",
"TargetPageId": "6b0d2f47-0000-4000-8000-000000001202"
},
{
"Label": "Prendre rendez-vous",
"TargetKind": "url",
"Href": "https://example.com/rdv",
"Highlighted": true
}
]
}
""", Encoding.UTF8, "application/json");
using HttpResponseMessage response = await client.SendAsync(request);
string result = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
throw new HttpRequestException(result);const response = await fetch("https://staging-api.docage.com/WebSites/c3e8a915-0000-4000-8000-000000001301", {
method: "PUT",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json"
},
body: JSON.stringify({
"HomePageId": "6b0d2f47-0000-4000-8000-000000001201",
"Navigation": [{"Label": "Nos offres", "TargetKind": "page", "TargetPageId": "6b0d2f47-0000-4000-8000-000000001202"}, {"Label": "Prendre rendez-vous", "TargetKind": "url", "Href": "https://example.com/rdv", "Highlighted": true}]
})
});
const text = await response.text();
if (!response.ok) throw new Error(text);
const result = text ? JSON.parse(text) : null;- L'accueil est une page de contenu du site.
- Le menu compte douze liens au plus ;
Highlightedfait d'un lien un bouton. Un blocnavigationdont laSourceestsiteaffiche ce menu. Il vaut dès qu'il est enregistré, sans republier les pages.
L'en-tête et le pied
POST https://staging-api.docage.com/WebSites/{id}/Parts/Header (ou Footer) crée une page du site avec un contenu par défaut, la désigne comme l'en-tête (ou
le pied) du site, et rend son identifiant ; un site qui en a déjà un rend celui-là. Composez-la comme une page, puis publiez-la : sa
version publiée entoure chaque page du site.
L'adresse du site
GET https://staging-api.docage.com/WebSites/{id}/State rend l'adresse du site (Address, /p/{organisation}/{site}) et IsServed : son accueil est choisi
et publié.
Les pièges
- Un bloc d'un
Typeinconnu est perdu sans erreur : relisez la page après l'avoir écrite. - Une page ne porte que des sections au premier niveau, une section ne contient pas de section, et un champ à remplir n'y a pas sa
place (
422, qui nomme le bloc) : embarquez un formulaire. - Une image ou une vidéo se dépose avant d'être citée (§ 4) : un bloc ne cite qu'un fichier déjà sur la page ou sur son modèle
(
422sinon), et une page neuve s'enregistre avant de recevoir un fichier. Une vidéo en ligne n'a pas besoin de fichier. - Un fichier déposé et jamais cité ne reste pas : le premier enregistrement des blocs, un jour au moins après son dépôt, le supprime.
- Un formulaire embarqué est un modèle de formulaire web de l'organisation ; une page visée par un bouton ou un lien, une page
de l'organisation (
422sinon). - Ce que voient les visiteurs change dans la minute : une publication, une dépublication, le menu du site.
Status,PublishedAtUtcetPublishedVersionsont écrits par le serveur : envoyés, ils sont ignorés sans erreur.