API REST

API Demandes de document

API v120 septembre 2026·11 min de lecture

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 : synchrone ou asynchrone (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, ou error (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, ou error), et file (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éé :

422 Unprocessable Content · application/json
{
  "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

POST/api/v1/document_requests

En asynchrone

cURL
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"
    }
  }'
200 OK · application/json
{
  "id": 100004,
  "tag": "Batch#1",
  "type": "asynchrone",
  "sandbox_mode": false,
  "status": "in_progress"
}

En synchrone

cURL
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"
    }
  }'
200 OK · application/json
{
  "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

ChampTypeRequisNotes
typestringouisynchrone ou asynchrone
prioritystringnoncritical, default, ou low ; forcé à low en sandbox
tagstringnondéfaut "" ; le seul identifiant que vous choisissez, et par lequel vous pouvez retrouver la demande
document_generationsarrayouiune seule entrée en synchrone, une ou plusieurs en asynchrone
document_generations[].template_idintegerouidoit être publié
document_generations[].tagstringnondéfaut ""
document_generations[].variablesobject ou nullnonobjet 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é.

ÉchecStatutNotes
type422 "Le type de génération demandée n'est pas valide. Valeurs acceptée : synchrone / asynchrone"
priority422 {"error": "..."}, nomme les valeurs acceptées
Requête synchrone portant plus d'une entrée422 "Vous ne pouvez générer qu'un seul document à la fin avec cette route."
webhook_url422 voir Synchrone ou asynchrone
document_generations422 "Le(s) paramètre(s) requis suivants sont manquants : document_generations"
template_id422 "Le(s) paramètre(s) requis suivants sont manquants : template_id"
template_id422 nomme l'identifiant ; un mauvais identifiant et celui de quelqu'un d'autre répondent à l'identique
content422
Le contenu rendu dépasse la limite de taille de l'organisation422 voir Limites
variables422
document_request400 {"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 :

422 Unprocessable Content · application/json
{
  "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
curl "https://app.doclift.io/api/v1/document_requests?tag=Invoice%2342" \
  --header "X-Api-Key: <your-api-key>"
SituationCe que vous recevezCe qu'il faut faire
Votre connexion se coupe avant la réponserien : une erreur réseau de votre clientretrouvez la demande par son tag avant de rejouer
429 {"error": "…"} avec un en-tête Retry-Afterattendez 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 :

429 Too Many Requests · Retry-After: 1
{
  "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 :

Workflow document_generations entry
{
  "template_id": 100050,
  "variables": {
    "investor_type": "natural",
    "country": "FR",
    "investments": [{ "product": "SCPI", "amount": "10 000 €" }]
  },
  "tag": ""
}
ÉchecDéclencheur
Variable requise manquanteune variable déclarée required n'a aucune clé du tout dans le payload
Collection malforméela valeur d'une variable de type collection n'est pas un tableau d'objets plats
Valeur non scalaireune variable non-collection a reçu un tableau ou un objet
Collection surdimensionnéeune collection porte plus de 200 lignes
Champ de ligne requis manquantun champ requis d'une ligne de collection n'a aucune clé dans cette ligne
Exclut toutes les sectionsles conditions du payload ne laissent rien à afficher
N'imprime rienles 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

GET/api/v1/document_requests
cURL
curl https://app.doclift.io/api/v1/document_requests \
  --header "X-Api-Key: <your-api-key>"
200 OK · application/json
[
  {
    "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
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.

StatutCorpsQuand
200 tableautoujours, y compris pour un compte vide
403 {"error": "..."}clé d'API absente, inconnue, ou désactivée

Consulter une demande de document

GET/api/v1/document_requests/:id
cURL
curl https://app.doclift.io/api/v1/document_requests/100004 \
  --header "X-Api-Key: <your-api-key>"
200 OK · application/json
{
  "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.

StatutCorpsQuand
200 demande de document, vue détailléeappartient à 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

LimiteValeurPortée
Taille du contenu rendu800 000 caractères par défaut, configurable par organisationmodè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 demande1 en synchrone, sans limite en asynchronevoir Synchrone ou asynchrone
Demandes synchrones simultanément en attentepar organisation, ajustable sur demandeau-delà : 429 avec Retry-After
Nouvelles tentatives de livraison de webhookconfigurable par organisationvoir 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.