Workflows
Modèles, publication et document
Capabilities, la ressource modèle de workflow, validate, le contrat de payload, publier/retirer, et l'export/import du document complet.
Chaque endpoint ci-dessous est sous /api/v1/workflows. Lisez
Workflows d'abord pour le modèle objet et l'ordre dans
lequel ces appels sont censés s'enchaîner. Cette page et ses deux voisines
forment la référence champ par champ : celle-ci couvre capabilities, la
ressource modèle, la publication, et l'export/import du document complet ;
Sections et thème couvre l'arborescence des
sections, les arrière-plans 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.
Comportement commun
Chaque appel ci-dessous porte le même en-tête X-Api-Key que le reste de
l'API (voir Bases de l'API), plus trois règles
propres à cet espace :
- L'organisation doit être activée pour les workflows. Sinon, chaque
appel (lectures comprises) répond
403 {"error": "<message>"}. C'est un indicateur propre à l'organisation activé par Doclift, pas un palier de forfait qu'une clé peut débloquer elle-même. - Chaque
:idest cadré sur les modèles de workflow non archivés de l'organisation appelante. L'id d'un modèle personnalisé ou d'un formulaire à remplir, l'id d'un workflow archivé, ou l'id d'un workflow d'une autre organisation sont tous indiscernables d'un id qui n'existe pas :404 {"error": "<message>"}. - Une écriture est refusée tant que le constructeur du dashboard détient
le verrou d'édition. C'est un verrou de session de trois minutes pris
par un humain en train d'éditer le même workflow dans le navigateur :
409 {"error": "<message>"}. Les lectures ne sont jamais bloquées par ce verrou ; une fois le verrou périmé (plus de trois minutes), l'écriture repasse sans que vous n'ayez rien à faire.
Un corps de requête auquel manque sa clé racine attendue (template,
section, variable, dataset, image, background, theme, ou
document, selon l'endpoint) répond 400 {"error": "<message>"} avant même
que les règles ci-dessus soient vérifiées : le même schéma que le reste de
l'API, voir Codes de réponse.
Capabilities
Récupérer les capabilities
curl https://app.doclift.io/api/v1/workflows/capabilities \
--header "X-Api-Key: <your-api-key>"
{
"contract_version": 1,
"section": {
"kinds": ["group", "rich_content", "image_with_variable"],
"carries": {
"group": { "children": true, "content": false, "placements": false, "image": false, "running_titles": "root only" },
"rich_content": { "children": false, "content": true, "placements": false, "image": false, "running_titles": false },
"image_with_variable": { "children": false, "content": false, "placements": true, "image": true, "running_titles": false }
}
},
"condition": {
"operators": {
"single_value": ["eq", "not_eq", "contains"],
"multi_value": ["in", "not_in"],
"valueless": ["blank", "present"]
},
"collection_operators": ["present", "blank"]
},
"placement": {
"keys_by_kind": { "text": ["variable"], "checkbox": ["variable"], "static_text": ["value"] },
"unknown_keys": "dropped without error: the write answers 200"
},
"variable": {
"field_types": ["text", "checkbox", "radio", "select", "collection"],
"collection": { "fields": { "keys": ["name", "description", "required", "seed_value"] } }
},
"theme": {
"blocks": ["paragraph", "h1", "h2", "h3"],
"properties": ["font_family", "font_size", "color", "line_height"],
"font_size": { "min": 6, "max": 96, "unit": "pt" },
"line_height": { "min": 1.0, "max": 2.5, "unit": null }
},
"page": {
"size_mm": { "portrait": [210, 297], "landscape": [297, 210] },
"margin_sides": ["top", "right", "bottom", "left"]
},
"content": {
"allowed_tags": ["..."],
"allowed_attributes": ["..."],
"tokens": {
"text": "<variable class=\"editor-text-variable non-editable-content editor-parsed\">investor_name</variable>"
}
},
"authoring": {
"fonts": { "faces": ["..."], "substitutes": {} },
"font_sizes_pt": [8, 10, 12, 14, 16, 18, 24, 36, 48],
"table": { "layout_row_class": "wf-layout-row" }
},
"anomalies": [{ "type": "broken_reference", "blocking": true }],
"limits": {
"sections": 200,
"image_bytes": 10485760,
"document_length": 800000,
"tree_depth": 3,
"numbering_start_max": 100,
"dataset_name_length": 60
}
}
Aucun paramètre, 200 toujours une fois authentifié et l'organisation
activée. Rien dans cette réponse n'est une valeur produit figée à
l'exception des clés à forme constante (section.kinds,
condition.operators, placement.keys_by_kind, page.size_mm,
variable.field_types, theme.blocks/properties, anomalies).
limits.sections, limits.image_bytes et limits.document_length sont
les propres plafonds de cette organisation, et content.allowed_tags/
allowed_attributes ainsi que authoring.fonts sont lus depuis
l'application en cours d'exécution plutôt que figés au démarrage. Voir
Capabilities fait foi, ce n'est pas une copie.
content.tokens.text est le HTML exact et littéral qu'un token de variable
texte doit porter à l'intérieur du contenu d'une section. Tout ce qui s'en
approche sans être identique (une classe manquante, une balise erronée) est
un token inerte qui survit à l'assainissement et affiche son propre nom de
variable. Voir Pièges pour la liste complète des
écritures qui font silencieusement moins que ce qui a été demandé.
Modèles
Un workflow est une ressource modèle avec category: "workflow",
forcée côté serveur : le POST /api/v1/templates classique ne peut pas en
créer un, et aucun des endpoints d'écriture classiques ne le touche jamais
(voir
Les écritures n'atteignent que les modèles personnalisés).
Il ne porte jamais le champ historique content.
Lister les modèles
curl https://app.doclift.io/api/v1/workflows/templates \
--header "X-Api-Key: <your-api-key>"
[
{
"id": 100050,
"title": "Subscription bulletin",
"description": "Investor onboarding document",
"published": false,
"orientation": "portrait",
"created_at": "2026-01-10 09:00:00 +0100",
"updated_at": "2026-02-03 11:15:00 +0100"
}
]
Les modèles de workflow non archivés de l'organisation appelante, les plus récemment mis à jour en premier, paginés à 30 par page (mêmes en-têtes que Pagination).
| Statut | Corps | Quand |
|---|---|---|
| 200 | tableau | succès |
| 403 | {"error": "..."} | clé d'API manquante/invalide, ou workflows non activés pour cette organisation |
Afficher un modèle
curl https://app.doclift.io/api/v1/workflows/templates/100050 \
--header "X-Api-Key: <your-api-key>"
{
"id": 100050,
"title": "Subscription bulletin",
"description": "Investor onboarding document",
"published": false,
"orientation": "portrait",
"margin_top": 10,
"margin_right": 10,
"margin_bottom": 10,
"margin_left": 10,
"created_at": "2026-01-10 09:00:00 +0100",
"updated_at": "2026-02-03 11:15:00 +0100",
"sections_count": 14,
"variables_count": 6,
"being_edited": false
}
being_edited vaut true tant que le constructeur du dashboard détient le
verrou d'édition : la seule raison pour laquelle une écriture sur ce
workflow peut être refusée pour une cause que le payload lui-même ne
laisse rien deviner. Vérifiez-le avant de retenter un 409.
| Statut | Corps | Quand |
|---|---|---|
| 200 | modèle | succès |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 403 | {"error": "..."} | voir Comportement commun |
Créer un modèle
curl https://app.doclift.io/api/v1/workflows/templates \
--request POST \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"template": {
"title": "Subscription bulletin",
"description": "Investor onboarding document",
"orientation": "portrait",
"margin_top": 10,
"margin_right": 10,
"margin_bottom": 10,
"margin_left": 10
}
}'
{
"id": 100050,
"title": "Subscription bulletin",
"description": "Investor onboarding document",
"published": false,
"orientation": "portrait",
"margin_top": 10,
"margin_right": 10,
"margin_bottom": 10,
"margin_left": 10,
"created_at": "2026-01-10 09:00:00 +0100",
"updated_at": "2026-01-10 09:00:00 +0100",
"sections_count": 0,
"variables_count": 0,
"being_edited": false
}
category est toujours forcé à "workflow", quoi que vous envoyiez.
| Champ | Type | Requis | Notes |
|---|---|---|---|
| title | string | oui | |
| description | string | oui | |
| orientation | string | non | portrait ou landscape, défaut portrait |
| margin_top | integer | non | chacun >= 5, défaut 10 |
| Statut | Corps | Quand |
|---|---|---|
| 201 | modèle | succès |
| 422 | {"errors": ["..."]} | titre/description vide, ou une marge sous 5 |
| 400 | {"error": "..."} | corps sans la clé racine template |
| 403 | {"error": "..."} | voir Comportement commun |
Mettre à jour un modèle
Mêmes champs qu'à la création. 200 avec la même forme en cas de succès ;
le titre enregistré reste inchangé sur un 422.
| Statut | Corps | Quand |
|---|---|---|
| 200 | modèle | succès |
| 422 | {"errors": ["..."]} | mêmes validations qu'à la création |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Supprimer un modèle
curl https://app.doclift.io/api/v1/workflows/templates/100050 \
--request DELETE \
--header "X-Api-Key: <your-api-key>"
204 No Content. Ceci archive le modèle plutôt que de le supprimer. Les
générations déjà produites continuent de pointer vers le modèle qui les a
créées.
| Statut | Corps | Quand |
|---|---|---|
| 204 | aucun | succès |
| 404 | {"error": "..."} | pas un workflow, déjà archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Valider un workflow
Un POST qui n'écrit rien : il n'est pas soumis au verrou d'édition, et
répond même pendant que le constructeur le détient. Le corps est
entièrement optionnel : content (tableau de fragments HTML sous forme de
chaînes) et scope ("content" ou "running_title") exécutent une
simulation de l'assainisseur sur des fragments que vous n'avez pas encore
enregistrés. anomalies et stored décrivent ce qui est déjà enregistré
sur ce workflow, indépendamment de tout content que vous transmettez.
Voir Valider pour la forme complète
de la réponse, les tableaux des types d'anomalies bloquantes/non
bloquantes, et le déroulé de la simulation (c'est le même endpoint,
documenté là en entier pour ne pas être répété sur chaque page qui le
mentionne).
| Champ | Type | Requis | Notes |
|---|---|---|---|
| content | tableau de strings | non | fragments de simulation ; 422 si présent mais pas un tableau de strings |
| scope | string | non | content ou running_title ; 422 pour toute autre valeur |
| Statut | Corps | Quand |
|---|---|---|
| 200 | voir Valider | toujours, dès lors que la requête elle-même est bien formée |
| 422 | {"errors": ["..."]} | content présent mais pas un tableau de strings, ou scope hors de ses deux valeurs |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 403 | {"error": "..."} | voir Comportement commun |
Récupérer le contrat de payload
Lecture seule, aucun paramètre. Renvoie exactement les variables dont un
payload de génération pour ce workflow a besoin, plus un exemple prêt à
envoyer. Voir
Le contrat de payload
pour la forme complète de la réponse et la façon d'utiliser
example.document_request.
| Statut | Corps | Quand |
|---|---|---|
| 200 | voir Le contrat de payload | succès |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 403 | {"error": "..."} | voir Comportement commun |
Publication
Une ressource singleton sous le modèle : published est un simple champ
booléen du modèle, mais seule cette route est autorisée à le faire
basculer pour un workflow. Voir
Publier et dépublier
pour le déroulé complet de la requête/réponse et la boucle qui vous y mène
(validate → corriger → validate de nouveau → publication).
Publier un workflow
Exécute le même contrôle d'intégrité que celui que lit validate. Si une
anomalie bloquante subsiste, l'écriture est refusée et rien ne change ;
chaque anomalie bloquante est listée, pas seulement la première. Les
anomalies non bloquantes (missing_background, empty_membership,
optional_conditioning) ne bloquent jamais cet appel : un workflow avec un
arrière-plan non défini se publie sans problème.
| Statut | Corps | Quand |
|---|---|---|
| 200 | {"published": true} | aucune anomalie bloquante ne subsiste |
| 422 | {"error": "<message>", "anomalies": [...]} | une anomalie bloquante subsiste ; rien n'est modifié |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Retirer un workflow
204 No Content. Jamais soumis à l'intégrité : un workflow dont les
références se sont cassées après sa publication est exactement celui qui
doit pouvoir être retiré sans d'abord devoir repasser un contrôle qu'il ne
réussirait peut-être plus.
Publier un workflow est ce qui le rend visible depuis
GET /api/v1/templates et générable depuis
POST /api/v1/document_requests ; voir
Types de modèles.
| Statut | Corps | Quand |
|---|---|---|
| 204 | aucun | succès, qu'il passe ou non actuellement le contrôle d'intégrité |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |
Document (export/import du workflow complet)
Une ressource singleton : le workflow entier (thème, marges, variables, jeux de données, images de contenu, et toute l'arborescence des sections, images incluses en base64) comme un seul objet JSON.
Exporter le document
curl https://app.doclift.io/api/v1/workflows/templates/100050/document \
--header "X-Api-Key: <your-api-key>"
{
"document": {
"format_version": 1,
"key": "subscription-bulletin",
"template": {
"title": "Subscription bulletin",
"description": "Investor onboarding document",
"orientation": "portrait",
"margin_top": 10,
"margin_right": 10,
"margin_bottom": 10,
"margin_left": 10,
"workflow_theme": { "paragraph": { "font_family": "Arial", "font_size": 12 } }
},
"variables": [{ "name": "investor_name", "field_type": "text", "required": true }],
"datasets": [{ "name": "sample_natural_person", "values": { "investor_name": "Jane Doe" } }],
"content_images": [{ "token": "a1b2c3", "file": "content-1.png" }],
"sections": [{ "kind": "group", "title": "Cover page", "children": [] }],
"pictures": {
"background-1.png": "<base64>",
"content-1.png": "<base64>"
}
},
"unresolved_tokens": []
}
unresolved_tokens nomme tout token cité dans le contenu qui ne résout
vers aucune image enregistrée : signalé ici plutôt que de produire
silencieusement une image cassée. Répond même pendant que le constructeur
détient le verrou d'édition, puisque c'est une lecture.
| 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 |
Remplacer le document
Un remplacement complet destructeur, pas une fusion
Les sections, variables et jeux de données de prévisualisation de la cible
sont effacés et reconstruits à partir du corps de la requête : tout ce que
le document ne porte pas disparaît. Si une section échoue à s'enregistrer
en cours de reconstruction, rien de l'écriture n'est appliqué : la cible
est laissée exactement comme avant le PUT, jamais à moitié reconstruite.
curl https://app.doclift.io/api/v1/workflows/templates/100050/document \
--request PUT \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"document": {
"format_version": 1,
"key": "subscription-bulletin",
"template": { "title": "Subscription bulletin", "description": "..." },
"variables": [],
"datasets": [],
"content_images": [],
"sections": [],
"pictures": {}
}
}'
{
"replacement": true,
"notice": "<message>",
"template": { "id": 100050, "title": "Subscription bulletin", "published": false },
"tree": [],
"datasets": 0,
"content_images": "<count or list, not pinned by this API's test suite>",
"backgrounds": "<count or list, not pinned by this API's test suite>",
"dangling_tokens": [],
"discarded_images": []
}
Le corps document est admis dans son intégralité, sans liste blanche
champ par champ : un thème, une condition et un placement ont la forme que
produit le constructeur du dashboard, pas un schéma figé, et une liste
d'autorisation ici ne ferait que dupliquer cette forme à maintenir en
parallèle. Deux comportements à connaître avant de s'appuyer dessus :
document.template.publishedest lu mais jamais appliqué. La cible garde sa propre valeurpublishedactuelle, quoi que dise le corps de la requête. Cela ferme une porte autour du contrôle d'intégrité propre àpublication, que cet endpoint n'exécute pas.- Les images de contenu citées depuis le contenu reçoivent de nouvelles copies enregistrées avec de nouveaux tokens sur la cible ; le contenu est réécrit pour pointer vers ces copies plutôt que vers les images du workflow source.
| Champ | Type | Requis | Notes |
|---|---|---|---|
| format_version | integer | oui | doit être une version que cette API prend encore en charge |
| key | string | oui | |
| template | object | oui | workflow_theme et les quatre marges vivent ici |
| variables | array | oui | remplace chaque variable de la cible |
| datasets | array | oui | remplace chaque jeu de données de prévisualisation de la cible |
| content_images | array | oui | le file de chaque entrée doit avoir une clé correspondante dans pictures |
| sections | array | oui | toute l'arborescence, remplaçant celle de la cible |
| pictures | object | oui | carte {filename => base64} pour chaque image citée ci-dessus |
La réponse rapporte aussi content_images et backgrounds de l'import ;
si chacun est un compte ou une liste n'est pas figé par la suite de tests
de cette API, donc traitez-les comme informatifs plutôt que comme un
contrat typé.
| Statut | Corps | Quand |
|---|---|---|
| 200 | corps ci-dessus | succès |
| 422 | {"errors": ["..."]} | format_version non pris en charge ; une entrée de content_images nommant un fichier absent de pictures ; une valeur de pictures qui n'est pas du base64 valide ; le corps n'est pas un Hash ; une clé requise manque ; la catégorie du document ne correspond pas à celle de la cible ; une section échoue sa validation à la reconstruction (retour en arrière) |
| 404 | {"error": "..."} | pas un workflow, archivé, ou d'une autre organisation |
| 409 | {"error": "..."} | verrou d'édition détenu |
| 403 | {"error": "..."} | voir Comportement commun |