Workflows
Sections et thème
L'arborescence des sections d'un workflow : chaque endpoint de section, les arrière-plans, et le thème typographique du document.
Chaque endpoint ci-dessous est sous /api/v1/workflows. Lisez
Workflows d'abord pour le modèle objet ; lisez
Modèles, publication et document pour
capabilities, la ressource modèle, et la publication. Cette page couvre
l'arborescence des sections et le thème ;
Variables et données couvre les variables,
les jeux de données de prévisualisation et les images de contenu.
Les workflows sont en bêta privée, disponible sur demande à [email protected] ; voir Workflows.
Thème
Voir Le modèle objet pour ce qu'est le
thème et pourquoi il voyage à l'intérieur de chaque instantané publié.
font-weight n'est délibérément pas une propriété modifiable ici : en
fixer une sur un titre écraserait le gras que le moteur de rendu lui donne
déjà, et sur un paragraphe cela n'apporterait rien de nouveau.
Récupérer le thème
curl https://app.doclift.io/api/v1/workflows/templates/100050/theme \
--header "X-Api-Key: <your-api-key>"
{
"theme": {
"paragraph": { "font_family": "Arial", "font_size": 12, "color": "#1a1a1a", "line_height": 1.4 },
"h1": { "font_family": "Arial", "font_size": 24, "color": "#000000" }
},
"blocks": ["paragraph", "h1", "h2", "h3"],
"properties": ["font_family", "font_size", "color", "line_height"],
"fonts": { "faces": [{ "family": "Arial", "weights": [400, 700] }], "substitutes": { "Calibri": "Liberation Sans" } },
"font_size": { "min": 6, "max": 96, "unit": "pt" },
"line_height": { "min": 1.0, "max": 2.5, "unit": null }
}
theme ne porte que les blocs effectivement déclarés : un bloc jamais
réglé est simplement absent, pas rempli par défaut dans la réponse. Répond
même pendant que le verrou d'édition est détenu.
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 403 | {"error": "..."} | voir Comportement commun |
Mettre à jour le thème
curl https://app.doclift.io/api/v1/workflows/templates/100050/theme \
--request PATCH \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"theme": {
"h1": { "font_family": "Arial", "font_size": 24, "color": "#000000" }
}
}'
{
"theme": {
"paragraph": { "font_family": "Arial", "font_size": 12, "color": "#1a1a1a", "line_height": 1.4 },
"h1": { "font_family": "Arial", "font_size": 24, "color": "#000000" }
},
"blocks": ["paragraph", "h1", "h2", "h3"],
"properties": ["font_family", "font_size", "color", "line_height"],
"fonts": { "faces": [{ "family": "Arial", "weights": [400, 700] }], "substitutes": {} },
"font_size": { "min": 6, "max": 96, "unit": "pt" },
"line_height": { "min": 1.0, "max": 2.5, "unit": null }
}
Fusion bloc par bloc, pas propriété par propriété
Un bloc que votre corps ne nomme pas reste exactement tel qu'enregistré. Un
bloc que votre corps nomme est écrit en entier : envoyer h1 seul ne
touche pas le paragraph enregistré, mais cela remplace bien chaque
propriété que h1 portait déjà par uniquement ce que vous avez envoyé pour
h1 cette fois.
Toute valeur qui ne s'interprète pas est retirée du thème enregistré plutôt
que de refuser toute la requête. La réponse que vous recevez est toujours
la valeur fraîchement rechargée et renormalisée, jamais un écho de ce que
vous avez envoyé : lire la réponse est le seul moyen de voir un abandon se
produire. Une famille de police non provisionnée, un line_height portant
une unité, un font_size hors de 6-96 pt, un color non hexadécimal, un
bloc ou une propriété inconnus, et tout font_weight sont tous abandonnés
de cette façon. Voir Pièges pour la liste
complète et à quoi ressemble chaque abandon dans la réponse.
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès, même si certaines valeurs ont été silencieusement abandonnées |
| 400 | {"error": "..."} | corps sans la clé racine theme |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Sections
Voir Le modèle objet pour ce que porte
un groupe par rapport à une section de contenu, et pourquoi une référence
cassée survit plutôt que d'être balayée. Le tableau ci-dessous est la
référence propre à cette page : l'arborescence compte trois niveaux au
maximum (groupe, groupe, section) et capabilities.limits.tree_depth
l'indique.
| Kind | children | content | placements | image | running_titles |
|---|---|---|---|---|---|
| group | oui | non | non | non | groupe racine seulement |
| rich_content | non | oui | non | non | non |
| image_with_variable | non | non | oui | oui | non |
Lister les sections
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections \
--header "X-Api-Key: <your-api-key>"
{
"tree": [
{
"id": 300100,
"kind": "group",
"title": "Investor identity",
"condition": null,
"children": [
{ "id": 300101, "kind": "rich_content", "title": "Identity paragraph", "content": "<p>...</p>" }
]
}
]
}
Toute l'arborescence, ordonnée et imbriquée.
| Statut | Corps | Quand |
|---|---|---|
| 200 | {"tree": [...]} | succès |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 403 | {"error": "..."} | voir Comportement commun |
Afficher une section
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections/300101 \
--header "X-Api-Key: <your-api-key>"
{
"id": 300101,
"kind": "rich_content",
"title": "Identity paragraph",
"content": "<p>Dear <variable class=\"editor-text-variable non-editable-content editor-parsed\">investor_name</variable></p>",
"layout": "inline",
"page_break": "continue",
"repeat_over": null,
"parent_id": 300100,
"position": 0,
"condition": null,
"running_titles": null,
"placements": null,
"image_url": null
}
Un seul nœud (pas de clé tree ici). reference (un champ réservé à
l'export/import) n'est délibérément jamais publié : rien ne le lit, donc le
publier mettrait dans le contrat un champ dont personne ne pourrait dire
quoi faire.
| Statut | Corps | Quand |
|---|---|---|
| 200 | section | succès |
| 404 | {"error": "..."} | la section appartient à un autre workflow, ou le workflow lui-même est hors du périmètre |
| 403 | {"error": "..."} | voir Comportement commun |
Créer une section
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections \
--request POST \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"section": {
"kind": "group",
"title": "Investor identity",
"position": 0,
"children": [
{
"kind": "rich_content",
"title": "Identity paragraph",
"content": "<p>Dear <variable class=\"editor-text-variable non-editable-content editor-parsed\">investor_name</variable></p>"
}
]
}
}'
{
"section": { "id": 300100, "kind": "group", "title": "Investor identity", "children": ["..."] },
"tree": ["..."]
}
children est parcouru récursivement : un groupe et toute sa
sous-arborescence peuvent être créés en un seul appel, jusqu'au troisième
niveau, de sorte qu'un appelant ne rejoue pas N créations en laissant un
groupe à moitié constitué si l'une d'elles échoue. La réponse porte à la
fois le nœud créé et l'arborescence entière, pour que vous voyiez les
frères et sœurs renumérotés.
| Champ | Type | Requis | Notes |
|---|---|---|---|
| kind | string | oui | voir Sections ; lisez section.kinds depuis capabilities |
| title | string | oui | |
| content | string | non | rich_content seulement (refusé sur group/image_with_variable) |
| condition | object | non | {match: "all"|"any", rules: [...]} |
| layout | string | non | inline ou full_page |
| page_break | string | non | continue, new_page, ou own_page |
| repeat_over | string | non | nom d'une variable collection ; refusé si imbriqué dans une autre répétition |
| placements | array | non | image_with_variable seulement ; chaque entrée accepte id, kind, variable, value, x, y, width, height, align, font_size, color, font_family, font_weight |
| running_titles | object | non | groupes racines seulement |
| parent_id | integer | non | doit nommer un groupe de ce même workflow, à une profondeur légale |
| position | integer | non | ordre entre frères et sœurs |
| children | array | non | récursif, même forme, jusqu'au troisième niveau |
kind sur une entrée de placement sélectionne le type de placement,
pas le kind de la section au-dessus : la liste actuelle et faisant foi est
capabilities.placement.keys_by_kind, par exemple text et checkbox
(portant toutes deux variable) et static_text (portant value). Elle
décide seulement si l'entrée porte variable (liée à une variable
déclarée) ou value (une chaîne littérale) ; les clés de
positionnement/style (x, y, width, height, align, font_size,
color, font_family, font_weight) s'appliquent à chaque kind, et
toute clé de géométrie laissée absente est lue comme 0. Le fait qu'une
variable radio/select puisse être placée n'est pas précisé par cette
référence. Lisez capabilities.placement.keys_by_kind en direct plutôt que
de supposer une liste figée.
Une clé de placement mal orthographiée est supprimée, pas refusée
Seules les clés exactes listées ci-dessus sont conservées. Tout le reste
répond 201 et est silencieusement absent de la section enregistrée. Le
libellé s'affiche comme rien à la position qu'il aurait dû occuper. Voir
Pièges pour ceci et toute autre écriture qui
répond 200 en faisant discrètement moins que demandé.
Le contenu est assaini à l'écriture selon les listes blanches que publie capabilities : une balise ou un attribut hors liste est retiré silencieusement, comme la clé de placement mal orthographiée ci-dessus.
| Statut | Corps | Quand |
|---|---|---|
| 201 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | titre vide ; contenu sur un groupe ou une section image ; placements sur autre chose que image_with_variable ; parent_id inconnu, étranger, ou à mauvaise profondeur ; répétition imbriquée ; running_titles sur un groupe non racine ; un start de numérotation hors de 1-100 ; le plafond de sections de l'organisation atteint |
| 400 | {"error": "..."} | corps sans la clé racine section |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Mettre à jour une section
Mêmes champs qu'à la création, moins les champs structurels (kind,
parent_id, position sont le travail de déplacer).
Envoyer repeat_over: "" est lu comme « pas de répétition ».
{
"section": { "id": 300101, "title": "Identity paragraph", "content": "<p>...</p>" },
"tree": ["..."]
}
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | mêmes validations qu'à la création, là où c'est pertinent |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Déplacer une section
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections/300101/move \
--request PATCH \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{ "section": { "parent_id": 300100, "position": 1 } }'
{
"section": { "id": 300101, "parent_id": 300100, "position": 1 },
"tree": ["..."]
}
Change le parent et/ou le rang ; les frères et sœurs, sur l'ancien comme sur le nouveau parent, sont renumérotés.
| Champ | Type | Requis | Notes |
|---|---|---|---|
| parent_id | integer | non | doit nommer un groupe de ce workflow à une profondeur légale |
| position | integer | non | rang entre frères et sœurs |
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | parent_id inconnu, ou déplacer un groupe qui contient déjà un groupe vers un autre groupe |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Dupliquer une section
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections/300100/duplicate \
--request POST \
--header "X-Api-Key: <your-api-key>"
{
"section": { "id": 300200, "kind": "group", "title": "Investor identity" },
"tree": ["..."]
}
Copie en profondeur le nœud et toute sa sous-arborescence à l'intérieur du même modèle, arrière-plans compris. La copie est ajoutée à la fin de l'arborescence.
| Statut | Corps | Quand |
|---|---|---|
| 201 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | le plafond de sections de l'organisation serait dépassé par la copie |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Supprimer une section
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections/300101 \
--request DELETE \
--header "X-Api-Key: <your-api-key>"
{ "tree": ["..."] }
Supprimer un groupe emporte toute sa sous-arborescence avec lui. Pas de
clé "section" dans la réponse : le nœud nommé dans l'URL n'existe plus.
Les frères et sœurs suivants sont renumérotés.
| Statut | Corps | Quand |
|---|---|---|
| 200 | {"tree": [...]} | succès |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Définir l'arrière-plan d'une section
Valide uniquement sur une section image_with_variable. Deux formes
d'import : un import multipart sous background[file], ou un corps JSON
avec filename, content_type, et data (base64 brut ou une URI data:
complète).
curl https://app.doclift.io/api/v1/workflows/templates/100050/sections/300150/background \
--request PATCH \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"background": {
"filename": "cover.png",
"content_type": "image/png",
"data": "data:image/png;base64,<...>"
}
}'
{
"section": { "id": 300150, "kind": "image_with_variable", "image_url": "/workflows/images/9f2a1c..." },
"tree": ["..."]
}
Chaque import passe par le même pipeline : réencodé en WebP, redimensionné
à au maximum 2480 px (arrière-plans) ou 1654 px (images de contenu), et
vérifié contre le plafond d'octets de l'organisation. Les arrière-plans
imposent en plus une largeur minimale (le
workflow_image_min_width propre à l'organisation, défaut 800 px),
puisqu'un arrière-plan est étiré à la largeur de la page ; une image de
contenu est placée à la taille que vous lui donnez, elle n'a donc pas de
plancher.
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | pas une section image_with_variable ; missing (aucun fichier/octet) ; too_large (au-dessus du plafond d'octets de l'organisation) ; wrong_type (vérifié contre les octets réencodés, pas le content-type déclaré) ; too_narrow (sous la largeur minimale) |
| 400 | {"error": "..."} | corps sans la clé racine background |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Supprimer l'arrière-plan d'une section
{
"section": { "id": 300150, "kind": "image_with_variable", "image_url": null },
"tree": ["..."]
}
Ceci remet la référence à néant plutôt que de changer quoi que ce soit : le fichier sous-jacent reste dans le stockage, jamais supprimé, car un instantané publié doit continuer de se rendre comme il se rendait le jour de sa publication.
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | pas une section image_with_variable |
| 404 | {"error": "..."} | section ou workflow hors du périmètre |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |