API REST
API Demandes de document
Référence complète de l'API Demandes de document de Doclift : génération synchrone et asynchrone, le payload, les statuts, les erreurs de validation et les limites.
L'objet demande de document
- id : identifiant unique
- tag : chaîne libre que vous envoyez, renvoyée telle quelle
- type :
synchroneouasynchrone(orthographiés tels quels, pas les termes anglais « synchronous »/« asynchronous ») ; la valeur que vous envoyez à la création, et que la réponse vous restitue telle quelle - sandbox_mode : défini une fois pour toutes à la création, d'après l'environnement de la clé appelante
- status :
in_progress,success, ouerror(le résultat global de la requête) - documents_generations : présent sur l'endpoint de liste, sur
l'endpoint de détail, et sur une réponse de création synchrone ;
absent d'une réponse de création asynchrone, puisque rien n'a encore
été rendu au moment où elle répond ; chaque entrée a
id,tag,generated_at,created_at,generation_status(created,in_progress,success, ouerror), etfile(filename,url,size)
template_id accepte un modèle custom, fillable_form, ou workflow,
du moment qu'il est publié. La forme du payload est identique pour les
trois. Les modèles workflow portent un contrat supplémentaire par-dessus
tout ce qui suit. Voir Modèles workflow.
Synchrone ou asynchrone
type: "synchrone" rend exactement un document et répond 200 OK
avec ce document déjà joint : id, tag, type, timestamp, et
documents_generations. Vous n'avez rien d'autre à implémenter : ni
webhook à exposer, ni URL à configurer, ni second chemin de code. C'est
le mode à choisir quand un utilisateur attend son document devant son
écran.
type: "asynchrone" accepte une ou plusieurs entrées dans
document_generations, met le tout en file, et répond immédiatement avec
id, tag, type, sandbox_mode, et status: "in_progress" :
aucune clé documents_generations du tout, puisque rien n'a encore
été rendu. Les documents terminés arrivent plus tard sous forme de
webhook. Voir Webhooks. C'est le mode des lots, et le
seul qui accepte plusieurs documents dans une même demande.
La génération asynchrone nécessite une URL de webhook configurée sur l'application externe appelante ; sans elle, la requête est refusée avant même que quoi que ce soit soit créé :
{
"error": "Veuillez ajouter une URL de webhook à cette application externe afin de pouvoir utiliser la génération en asynchrone. Ajoutez-en une depuis votre compte Doclift.io."
}
Un seul document en synchrone
Une demande synchrone portant plus d'une entrée dans
document_generations est refusée en 422. Les documents d'une même
demande sont rendus l'un après l'autre : un lot ferait attendre l'appelant
aussi longtemps que la somme de ses rendus. Pour plusieurs documents,
utilisez l'asynchrone.
Combien de temps attendre
Une réponse synchrone arrive en quelques secondes dans l'immense majorité des cas. Il n'y a pas de délai maximal imposé par l'API, mais votre propre réseau en impose souvent un : beaucoup de liaisons coupent une connexion restée silencieuse au-delà d'une minute environ, sans que ni vous ni Doclift n'y puissiez quoi que ce soit.
Une demande dont vous n'avez jamais reçu la réponse n'est pas perdue. Voir Quand la réponse ne vous parvient pas.
La sandbox force la priorité à low
priority (critical, default, ou low, défaut critical) définit le
rang de cette demande parmi les autres en attente de génération. En mode
sandbox, elle est toujours forcée à low, quelle que soit la valeur
envoyée.
Créer une demande de document
En asynchrone
curl https://app.doclift.io/api/v1/document_requests \
--request POST \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"document_request": {
"type": "asynchrone",
"document_generations": [
{
"template_id": 100013,
"tag": "Invoice#42",
"variables": {
"client_name": "John Doe"
}
}
],
"tag": "Batch#1"
}
}'
{
"id": 100004,
"tag": "Batch#1",
"type": "asynchrone",
"sandbox_mode": false,
"status": "in_progress"
}
En synchrone
curl https://app.doclift.io/api/v1/document_requests \
--request POST \
--header "X-Api-Key: <your-api-key>" \
--header "Content-Type: application/json" \
--data '{
"document_request": {
"type": "synchrone",
"document_generations": [
{
"template_id": 100013,
"tag": "Invoice#42",
"variables": {
"client_name": "John Doe"
}
}
],
"tag": "Invoice#42"
}
}'
{
"id": 100005,
"tag": "Invoice#42",
"type": "synchrone",
"timestamp": "2026-09-20T15:23:24+02:00",
"documents_generations": [
{
"id": 100231,
"tag": "Invoice#42",
"generated_at": "2026-09-20T15:23:24+02:00",
"created_at": "2026-09-20T15:23:23+02:00",
"generation_status": "success",
"file": {
"filename": "Invoice-1615737313.pdf",
"url": "https://doclift-production.s3.eu-west-1.amazonaws.com/d207f8fe.pdf?[...]",
"size": 189241
}
}
]
}
L'URL du fichier est valable 2 heures. Passé ce délai, relisez la demande via Consulter une demande de document pour en obtenir une nouvelle.
Les champs
| Champ | Type | Requis | Notes |
|---|---|---|---|
| type | string | oui | synchrone ou asynchrone |
| priority | string | non | critical, default, ou low ; forcé à low en sandbox |
| tag | string | non | défaut "" ; le seul identifiant que vous choisissez, et par lequel vous pouvez retrouver la demande |
| document_generations | array | oui | une seule entrée en synchrone, une ou plusieurs en asynchrone |
| document_generations[].template_id | integer | oui | doit être publié |
| document_generations[].tag | string | non | défaut "" |
| document_generations[].variables | object ou null | non | objet plat clé/valeur (voir Types de modèles) |
Il n'y a pas de clé d'idempotence : envoyer deux fois le même corps crée deux demandes de document indépendantes, chacune avec son propre statut.
Erreurs de validation
Chacun des cas suivants est vérifié dans l'ordre ; le premier échec court-circuite le reste et répond avant qu'aucun document ne soit généré.
| Échec | Statut | Notes |
|---|---|---|
| type | 422 | "Le type de génération demandée n'est pas valide. Valeurs acceptée : synchrone / asynchrone" |
| priority | 422 | {"error": "..."}, nomme les valeurs acceptées |
| Requête synchrone portant plus d'une entrée | 422 | "Vous ne pouvez générer qu'un seul document à la fin avec cette route." |
| webhook_url | 422 | voir Synchrone ou asynchrone |
| document_generations | 422 | "Le(s) paramètre(s) requis suivants sont manquants : document_generations" |
| template_id | 422 | "Le(s) paramètre(s) requis suivants sont manquants : template_id" |
| template_id | 422 | nomme l'identifiant ; un mauvais identifiant et celui de quelqu'un d'autre répondent à l'identique |
| content | 422 | |
| Le contenu rendu dépasse la limite de taille de l'organisation | 422 | voir Limites |
| variables | 422 | |
| document_request | 400 | {"error": "..."}, nomme le paramètre manquant le cas échéant |
Une clé d'API absente, invalide, ou désactivée répond 403 dans tous les
cas, comme partout ailleurs dans l'API. Voir
Codes de réponse.
Les messages ci-dessus sont ceux que l'API rend par défaut, en français.
Pour les recevoir en anglais, envoyez l'en-tête Accept-Language: en ;
voir Langue des réponses.
Erreurs de validation des variables
Sur un modèle fillable_form uniquement, chaque valeur fournie est
vérifiée par rapport aux allowed_values auto-détectées de sa variable
(voir Formulaires PDF). Une valeur hors de la
liste autorisée répond 422 en nommant chaque champ fautif :
{
"error": "Les valeurs fournies pour certaines variables ne sont pas valides (status: 'bad_value' (valeurs autorisées : approved, rejected)).",
"invalid_variables": [
{ "field": "status", "value": "bad_value", "allowed_values": ["approved", "rejected"] }
]
}
Les modèles custom et workflow ne sont pas vérifiés par rapport à
allowed_values au moment de la génération par cette étape.
Si la génération échoue pour toute autre raison une fois le payload par
ailleurs valide, vous l'observez via generation_status/generation_error de
GET /api/v1/document_requests/:id, ou via
generation_status: "error" du payload du webhook.
Quand la réponse ne vous parvient pas
Cette section ne concerne que le mode synchrone : en asynchrone, la réponse est immédiate et le résultat arrive par webhook.
Trois situations vous laissent sans réponse exploitable. Elles se
traitent toutes de la même façon, et c'est la règle à retenir : le
tag que vous avez envoyé permet de retrouver la demande.
curl "https://app.doclift.io/api/v1/document_requests?tag=Invoice%2342" \
--header "X-Api-Key: <your-api-key>"
| Situation | Ce que vous recevez | Ce qu'il faut faire |
|---|---|---|
| Votre connexion se coupe avant la réponse | rien : une erreur réseau de votre client | retrouvez la demande par son tag avant de rejouer |
| 429 | {"error": "…"} avec un en-tête Retry-After | attendez le délai indiqué et rejouez |
| 502 | {"code": "outcome_unknown", "tag": …, "retrieve": …} | la demande a peut-être abouti : vérifiez par son tag avant de rejouer |
| 504 | {"code": "generation_pending", "id": …, "tag": …, "retrieve": …} | la génération se poursuit ; relisez la demande à l'adresse indiquée |
Ne rejouez pas sans avoir vérifié
Il n'y a pas de clé d'idempotence. Une demande rejouée en crée une
seconde, indépendante, et vous facture un second document. Un 502 et
une connexion coupée signifient tous deux « je ne sais pas », pas
« ça a échoué ».
Trop de générations en attente
Une organisation ne peut avoir qu'un nombre limité de demandes synchrones en attente de traitement au même instant. Au-delà, les suivantes sont refusées immédiatement plutôt que mises à attendre :
{
"error": "Trop de générations synchrones sont déjà en attente pour votre organisation. Réessayez dans un instant."
}
Le créneau se libère dès qu'une génération se termine, et non au bout
d'une fenêtre de temps : un Retry-After d'une seconde suffit dans la
quasi-totalité des cas. Ce plafond ne s'applique pas à
l'asynchrone. Voir Limites.
Modèles workflow
Les modèles workflow sont générés via ce même endpoint. Il n'y a pas de route de génération workflow séparée. Les workflows sont en bêta privée, disponibles sur demande en écrivant à [email protected] ; voir Workflows pour la vue d'ensemble.
En plus de tout ce qui précède, le payload variables d'un workflow
porte un contrat de variable requise qu'aucune autre catégorie n'impose :
{
"template_id": 100050,
"variables": {
"investor_type": "natural",
"country": "FR",
"investments": [{ "product": "SCPI", "amount": "10 000 €" }]
},
"tag": ""
}
| Échec | Déclencheur |
|---|---|
| Variable requise manquante | une variable déclarée required n'a aucune clé du tout dans le payload |
| Collection malformée | la valeur d'une variable de type collection n'est pas un tableau d'objets plats |
| Valeur non scalaire | une variable non-collection a reçu un tableau ou un objet |
| Collection surdimensionnée | une collection porte plus de 200 lignes |
| Champ de ligne requis manquant | un champ requis d'une ligne de collection n'a aucune clé dans cette ligne |
| Exclut toutes les sections | les conditions du payload ne laissent rien à afficher |
| N'imprime rien | les sections survivent aux conditions mais s'assemblent en contenu vide |
Une clé présente avec une chaîne vide ou null compte comme répondue.
Seule l'absence de la clé déclenche la vérification de variable
manquante. Tous les échecs applicables sur le lot entier sont rapportés
ensemble dans un seul 422 {"error": "..."}, nommant chaque modèle et
variable concernés à la fois (utile lorsqu'un lot mélange
plusieurs générations de workflow). allowed_values n'est pas vérifié
pour les variables de workflow au moment de la génération.
Lister les demandes de document
curl https://app.doclift.io/api/v1/document_requests \
--header "X-Api-Key: <your-api-key>"
[
{
"id": 100004,
"tag": "Batch#1",
"type": "asynchrone",
"external_application": {
"id": 100000,
"name": "Production key",
"environment": "sandbox",
"active": true
},
"documents_generations": [
{
"id": 100230,
"tag": "Invoice#42",
"generated_at": "2024-01-15T10:35:02+01:00",
"created_at": "2024-01-15T10:35:00+01:00",
"generation_status": "success",
"file": {
"filename": "Invoice-1615737313.pdf",
"url": "https://doclift.s3.eu-west-1.amazonaws.com/d207f8fe.pdf?[...]",
"size": 40290
}
}
]
}
]
Liste les demandes de document de votre organisation, les plus récentes en premier. Cela découle de l'association au niveau de l'organisation, donc les demandes créées via d'autres applications externes de la même organisation apparaissent aussi, pas seulement celles de la clé appelante. Paginé à 30 par page ; voir Pagination.
Filtrer par tag
Le paramètre tag restreint la liste aux demandes portant exactement ce
tag. C'est le seul identifiant que vous choisissez vous-même et que vous
connaissez d'avance : c'est donc par lui que vous retrouvez une demande
dont vous n'avez jamais reçu la réponse.
curl "https://app.doclift.io/api/v1/document_requests?tag=Invoice%2342" \
--header "X-Api-Key: <your-api-key>"
La correspondance est exacte, pas partielle. Un tag inconnu répond 200
avec un tableau vide, jamais 404. Rien n'impose qu'un tag soit unique :
si vous en avez réutilisé un, la liste porte toutes les demandes qui le
partagent.
| Statut | Corps | Quand |
|---|---|---|
| 200 | tableau | toujours, y compris pour un compte vide |
| 403 | {"error": "..."} | clé d'API absente, inconnue, ou désactivée |
Consulter une demande de document
curl https://app.doclift.io/api/v1/document_requests/100004 \
--header "X-Api-Key: <your-api-key>"
{
"id": 100004,
"tag": "Batch#1",
"type": "asynchrone",
"external_application": {
"id": 100000,
"name": "Production key",
"environment": "sandbox",
"active": true
},
"documents_generations": [
{
"id": 100230,
"tag": "Invoice#42",
"generated_at": "2024-01-15T10:35:02+01:00",
"created_at": "2024-01-15T10:35:00+01:00",
"generation_status": "success",
"generation_duration": 1721,
"generation_error": null,
"sent_payload": { "client_name": "John Doe" },
"file": {
"filename": "Invoice-1615737313.pdf",
"url": "https://doclift.s3.eu-west-1.amazonaws.com/d207f8fe.pdf?[...]",
"size": 40290
},
"template": {
"id": 100013,
"title": "Invoice",
"description": "A template for invoices"
}
}
]
}
C'est la vue la plus riche : sent_payload (exactement ce que vous avez
envoyé), generation_duration, generation_error, et le template
source ne sont présents qu'ici, jamais dans la liste ni dans la réponse
de création. Les URLs de fichier sont valables 20 heures dans cette
vue, contre 2 heures partout ailleurs.
| Statut | Corps | Quand |
|---|---|---|
| 200 | demande de document, vue détaillée | appartient à votre organisation |
| 404 | {"error": "Record not found"} | identifiant inconnu ou d'une autre organisation |
| 403 | {"error": "..."} | clé d'API absente, inconnue, ou désactivée |
Limites
| Limite | Valeur | Portée |
|---|---|---|
| Taille du contenu rendu | 800 000 caractères par défaut, configurable par organisation | modèles custom et workflow ; fillable_form n'est jamais mesuré |
| Lignes de collection (workflow) | 200 | par variable de collection, par génération |
| Documents par demande | 1 en synchrone, sans limite en asynchrone | voir Synchrone ou asynchrone |
| Demandes synchrones simultanément en attente | par organisation, ajustable sur demande | au-delà : 429 avec Retry-After |
| Nouvelles tentatives de livraison de webhook | configurable par organisation | voir Webhooks |
Il n'y a aucune limitation de débit sur cette API : rien ne compte vos requêtes par minute ou par jour. La seule borne est le nombre de demandes synchrones que votre organisation peut avoir en attente de traitement au même instant, décrite ci-dessus. Elle se libère au rythme des générations qui se terminent, et ne s'applique pas à l'asynchrone. Voir Environnements.