Generez des documents PDF et DOCX a partir de templates Word. Envoyez un fichier Word avec des variables, ajoutez vos donnees en JSON, recevez le document final.
# Remplacez VOTRE_CLE par la cle obtenue a l'etape 2 curl -X POST https://docx.layerone.fr/render-document \ -H "X-API-Key: VOTRE_CLE" \ -F "template=@mon_template.docx" \ -F 'json_data={"client": "Acme Corp", "montant": "1 500 EUR"}' \ -o resultat.pdf
Si tout fonctionne, vous obtenez un fichier
resultat.pdf avec vos donnees injectees.
Ajoutez votre cle API dans le header X-API-Key de chaque
requete :
X-API-Key: votre_cle_api
Ne partagez jamais votre cle. Ne la mettez pas dans du code frontend (site web, app mobile). Utilisez-la uniquement depuis votre serveur.
Aucune authentification necessaire. Utile pour monitorer la disponibilite.
curl https://docx.layerone.fr/__health
{
"status": "ok",
"time": "2026-04-13T12:00:00Z",
"storage": { "templates_writable": true },
"libreoffice": { "binary_found": true }
}
Ajoutez ?deep=true pour un diagnostic complet (base
de donnees, espace disque).
Consultez combien de requetes vous avez utilisees ce mois-ci et votre limite.
curl https://docx.layerone.fr/usage-stats \ -H "X-API-Key: VOTRE_CLE"
{
"success": true,
"stats": {
"plan": "gratuit",
"quota": {
"limit": 20,
"used": 12,
"remaining": 8
},
"rate_limit": "5/minute",
"max_concurrent": 1,
"usage": {
"hourly": 3,
"daily": 12,
"burst_5min": 1
},
"thresholds": {
"hourly": 500,
"daily": 2000,
"burst": 50
},
"current_concurrent": 0
}
}
Le quota se reinitialise sur un cycle glissant de 30 jours a partir de votre souscription (pas le 1er du mois). Quand vous atteignez la limite, les requetes retournent une erreur 429 "Quota mensuel atteint".
Enregistrez un template pour ne pas le renvoyer a chaque requete.
Vous recevez un template_id a reutiliser.
curl -X POST https://docx.layerone.fr/client/templates \ -H "X-API-Key: VOTRE_CLE" \ -F "template=@mon_template.docx"
{
"template_id": "abc123",
"name": "mon_template.docx",
"status": "created",
"client_name": "mon_template",
"detected_tags": ["client_nom", "montant_ht", "lignes"],
"validation": "ok"
}
Remplace le fichier d'un template existant par une nouvelle version.
curl -X PUT https://docx.layerone.fr/client/templates/abc123 \ -H "X-API-Key: VOTRE_CLE" \ -F "template=@nouveau_template.docx"
Telecharge le fichier Word d'un template enregistre.
curl https://docx.layerone.fr/client/templates/abc123 \ -H "X-API-Key: VOTRE_CLE" \ -o template.docx
Supprime definitivement un template enregistre.
curl -X DELETE https://docx.layerone.fr/client/templates/abc123 \ -H "X-API-Key: VOTRE_CLE"
Retourne la liste de tous vos templates enregistres.
curl https://docx.layerone.fr/client/templates \ -H "X-API-Key: VOTRE_CLE"
Liste les versions archivees d'un template (sans le contenu
binaire) — triees par version decroissante. Authentifie par
la meme cle X-API-Key.
curl https://docx.layerone.fr/client/templates/abc123/versions \ -H "X-API-Key: VOTRE_CLE"
{
"template_id": "abc123",
"versions_count": 2,
"versions": [
{
"version_id": 12,
"version_number": 2,
"content_hash": "a1b2c3...",
"original_name": "devis_template.docx",
"size_bytes": 48213,
"archived_at": "2026-08-01T10:15:00Z"
},
{
"version_id": 11,
"version_number": 1,
"content_hash": "9f8e7d...",
"original_name": "devis_template.docx",
"size_bytes": 47900,
"archived_at": "2026-07-15T09:00:00Z"
}
]
}
Telecharge le contenu DOCX d'une version archivee specifique
(binaire, avec Content-Disposition attachment).
curl https://docx.layerone.fr/client/templates/abc123/versions/11 \ -H "X-API-Key: VOTRE_CLE" \ -o template_v1.docx
Restaure une version archivee comme contenu actif du template. La version courante est archivee avant l'ecrasement (rollback toujours possible).
curl -X POST https://docx.layerone.fr/client/templates/abc123/restore/11 \ -H "X-API-Key: VOTRE_CLE"
{
"template_id": "abc123",
"restored_from_version_id": 11,
"status": "restored"
}
Envoyez un template Word + des donnees JSON. L'API remplace les variables et retourne le document.
| Nom | Type | Description |
|---|---|---|
| template * | fichier |
Votre fichier Word (.docx) avec des variables
{{...}}
|
| template_id opt. | texte |
Ou bien l'ID d'un template deja enregistre (remplace
template)
|
| json_data * | JSON |
Les donnees a injecter. Ex: {"client": "Acme"}
|
| output_format opt. | texte | pdf (par defaut) ou docx |
| output_filename opt. | texte |
Nom du fichier retourne. Ex: devis_001.pdf
|
| json_data_base64 opt. | texte |
Alternative a json_data — transmettez vos
donnees JSON encodees en base64. Utile lorsque le JSON
contient des caracteres speciaux.
|
| document_type opt. | texte | Type de document pour filtrer le template. |
| compress_pdf opt. | booleen |
Compresse le PDF genere via Ghostscript (qualite "ebook").
Par defaut : true.
|
|
return_signature_fields opt. (parametre d'URL, pas de formulaire) |
booleen |
Passe en query string dans l'URL :
?return_signature_fields=true (ce n'est PAS un
champ -F du formulaire). Si true,
retourne les positions des champs de signature detectes dans
la reponse. Par defaut : false.
|
?return_signature_fields=true est passe
dans l'URL ET que des champs sont trouves :
retourne un JSON contenant pdf_base64 et
signature_fields.curl -X POST "https://docx.layerone.fr/render-document?return_signature_fields=true" \
-H "X-API-Key: VOTRE_CLE" \
-F "template=@devis_template.docx" \
-F 'json_data={"client": "Acme Corp"}'
curl -X POST https://docx.layerone.fr/render-document \
-H "X-API-Key: VOTRE_CLE" \
-F "template=@devis_template.docx" \
-F 'json_data={
"client": "Acme Corp",
"adresse": "12 rue de Paris, 75001",
"lignes": [
{"description": "Prestation A", "prix": "500 EUR"},
{"description": "Prestation B", "prix": "1 000 EUR"}
],
"total": "1 500 EUR"
}' \
-o devis.pdf
import requests, json
response = requests.post(
"https://docx.layerone.fr/render-document",
headers={"X-API-Key": "VOTRE_CLE"},
files={"template": open("devis_template.docx", "rb")},
data={"json_data": json.dumps({
"client": "Acme Corp",
"total": "1 500 EUR"
})}
)
with open("devis.pdf", "wb") as f:
f.write(response.content)
const form = new FormData();
form.append("template", fichierWord);
form.append("json_data", JSON.stringify({
client: "Acme Corp",
total: "1 500 EUR"
}));
const res = await fetch("https://docx.layerone.fr/render-document", {
method: "POST",
headers: { "X-API-Key": "VOTRE_CLE" },
body: form
});
const pdf = await res.blob();
En cas de succes (200), le corps de la reponse contient directement le fichier PDF ou DOCX.
Comme /render-document, mais produit un PDF conforme
a la norme Factur-X (PDF/A-3 + XML). Obligatoire pour la
facturation electronique B2B en France.
Parametres differents de
/render-document : pas de
output_format, pas de
return_signature_fields, pas de
compress_pdf. Seuls template ou
template_id, json_data (ou
json_data_base64), output_filename et
document_type sont acceptes. Le XML Factur-X est
genere automatiquement.
curl -X POST https://docx.layerone.fr/render-facturx \
-H "X-API-Key: VOTRE_CLE" \
-F "template=@facture_template.docx" \
-F 'json_data={
"numero_facture": "FA-2024-001",
"date_facture": "2024-03-15",
"client": {
"nom": "Acme Corp",
"adresse": "12 rue de Paris",
"code_postal": "75001",
"ville": "Paris",
"siret": "12345678901234"
},
"montant_ht_total": "1250.00",
"montant_tva_total": "250.00",
"montant_ttc_total": "1500.00"
}' \
-o facture.pdf
Le PDF est valide VeraPDF (PDF/A-3) et accepte par les plateformes comme Chorus Pro.
Transforme un PDF deja mis en page (par exemple une facture
generee par votre propre application) en Factur-X conforme
(PDF/A-3 + XML EN 16931),
sans repasser par un template. Le PDF fourni est converti
en PDF/A-3 et le XML (derive de json_data) y est
attache.
| Nom | Type | Description |
|---|---|---|
| pdf * | fichier | Le PDF deja mis en page (facture existante). |
| json_data * | JSON |
Memes cles que /render-facturx (fournisseur,
client, lignes, montants, numero_facture, date_facture).
|
| json_data_base64 opt. | texte | Alternative a json_data en base64. |
| output_filename opt. | texte | Nom du fichier retourne. |
curl -X POST https://docx.layerone.fr/attach-facturx \
-H "X-API-Key: VOTRE_CLE" \
-F "pdf=@facture_deja_generee.pdf" \
-F 'json_data={
"numero_facture": "FA-2024-001",
"date_facture": "2024-03-15",
"client": {
"nom": "Acme Corp",
"siret": "12345678901234"
},
"montant_ht_total": "1250.00",
"montant_tva_total": "250.00",
"montant_ttc_total": "1500.00"
}' \
-o facture_facturx.pdf
Reponse identique a /render-facturx : le
fichier PDF/A-3 conforme est retourne directement.
Un template est un fichier Word (.docx) classique avec des variables entre doubles accolades. L'API les remplace par vos donnees.
Dans votre fichier Word, ecrivez :
Bonjour {{client}}, Veuillez trouver ci-joint votre devis d'un montant de {{total}}. Date : {{date}} Reference : {{reference}}
Et envoyez ce JSON :
{
"client": "Acme Corp",
"total": "1 500 EUR",
"date": "15/03/2024",
"reference": "DEV-2024-001"
}
Vous pouvez utiliser la notation a points pour acceder a des sous-objets :
# Dans le Word Client : {{client.nom}} Ville : {{client.adresse.ville}} # Dans le JSON { "client": { "nom": "Acme Corp", "adresse": { "ville": "Paris" } } }
Pour generer plusieurs lignes (ex: lignes de devis), le moteur
(docxtpl/Jinja2) exige une syntaxe precise :
3 lignes de tableau Word distinctes (3 balises
<w:tr> separees). Un simple
{{lignes.description}} pose dans une seule cellule
ne fonctionnera pas — l'API ne duplique rien toute seule
sans cette syntaxe.
Ligne 1 du tableau Word (cellule vide, juste le tag) :
{%tr for item in lignes %}
Ligne 2 du tableau Word (les donnees, une cellule par colonne) :
{{ item.description }} | {{ item.prix }}
Ligne 3 du tableau Word (cellule vide, juste le tag) :
{%tr endfor %}
Et le JSON correspondant :
# JSON { "lignes": [ {"description": "Prestation A", "prix": "500 EUR"}, {"description": "Prestation B", "prix": "1 000 EUR"} ] }
Important : chacune des 3 lignes doit etre une vraie
ligne de tableau Word separee. Ne mettez jamais
{%tr for %} et {%tr endfor %} dans la meme
ligne de tableau — le rendu echouera.
Pour repeter un paragraphe entier (ex: une liste a puces ou
numerotee), le moteur (docxtpl/Jinja2) propose l'equivalent de
{%tr%} mais au niveau du paragraphe :
3 paragraphes Word distincts avec les balises
{%p for %} / {%p endfor %}. Un simple
{{liste}} pose dans un seul paragraphe
ne fonctionnera pas — l'API ne duplique rien toute seule
sans cette syntaxe.
Paragraphe 1 du document Word (paragraphe vide, juste le tag) :
{%p for prestation in prestations %}
Paragraphe 2 du document Word (les donnees — c'est ce paragraphe qui doit deja porter le style Word "Liste a puces" ou "Liste numerotee", applique dans Word avant d'inserer la balise) :
{{ prestation }}
Paragraphe 3 du document Word (paragraphe vide, juste le tag) :
{%p endfor %}
Et le JSON correspondant (liste de textes simples) :
# JSON { "prestations": [ "Nettoyage de chantier", "Evacuation des gravats", "Controle final" ] }
Variante avec des objets (une balise par champ dans le paragraphe 2,
ex :
{{ prestation.nom }} — {{ prestation.duree }}) :
# JSON { "prestations": [ {"nom": "Nettoyage de chantier", "duree": "2h"}, {"nom": "Evacuation des gravats", "duree": "1h30"} ] }
Les filtres de casse (maj, min,
cap, titre — voir la section "Filtres
disponibles" plus bas) s'appliquent aussi bien dans une liste que
partout ailleurs, y compris sur un champ imbrique (paragraphe 2 du
document Word) :
{{ prestation.nom | maj }} — {{ prestation.duree }}
La notation a points fonctionne comme partout ailleurs (voir "Variables imbriquees" plus haut) : chaque element de la liste peut etre un objet avec des sous-champs, y compris imbriques.
Important : chacun des 3 paragraphes doit etre un vrai
paragraphe Word separe, et chaque paragraphe
{%p for %} / {%p endfor %} ne doit
contenir que le tag — aucun autre texte. Un mot ajoute
dans le meme paragraphe que {%p for %} ou
{%p endfor %} est supprime silencieusement du
rendu final (il disparait, sans erreur). Ne mettez jamais
{%p for %} et {%p endfor %} dans le meme
paragraphe — le rendu echouera.
C'est le style du paragraphe (celui qui contient
{{ prestation }}) qui donne la puce ou le numero, pas
la syntaxe Jinja. Si ce paragraphe n'a pas de style de liste dans
Word, vous obtiendrez des lignes de texte simples, repetees mais
sans puce ni numero.
Plutot que de renvoyer le fichier Word a chaque fois, enregistrez-le
une fois avec POST /client/templates et reutilisez son
template_id dans vos appels.
Vous pouvez inserer des images dynamiques dans vos templates
grace au filtre photo(). Les images sont envoyees en
fichiers joints (multipart) et inserees a la taille souhaitee.
Ajoutez vos images comme fichiers multipart dans la requete, avec des
noms comme photo_0, photo_1, etc. Dans le
JSON, referencez-les par leur nom :
curl -X POST https://docx.layerone.fr/render-document \
-H "X-API-Key: votre_cle" \
-F "template_id=abc123" \
-F 'json_data={"photos": [{"image": "photo_0"}, {"image": "photo_1"}]}' \
-F "photo_0=@photo1.jpg" \
-F "photo_1=@photo2.jpg" \
-F "output_format=pdf"
Formats acceptes : JPEG et PNG uniquement (verifies sur le contenu reel du fichier, pas seulement sur l'extension). Taille maximale : 50 Mo par fichier.
photo()
Dans votre template Word, utilisez le filtre photo() pour
inserer et redimensionner les images :
| Dans le template | Resultat |
|---|---|
{{ image | photo(120) }} |
Image a 120 mm de large (hauteur auto) |
{{ image | photo(80, 60) }} |
Image a 80 × 60 mm (largeur × hauteur) |
{{ image | photo }} |
Taille par defaut (80 mm de large) |
{{ image | photo(160) }} |
Pleine largeur A4 (~160 mm) |
{{ image | photo(40) }} |
Miniature 40 mm |
Par defaut, les photos s'empilent verticalement :
# Dans le template Word {% for p in photos %} {{ p.image | photo(120) }} {% endfor %}
Pour afficher les photos cote a cote, inserez un tableau Word a 2 colonnes dans votre template et placez les tags dans les cellules :
# Tableau Word avec 2 cellules par ligne | {{ row.0.image | photo(75) }} | {{ row.1.image | photo(75) }} |
Envoyez les photos regroupees par lignes de 2 dans le JSON :
{
"rows": [
[{"image": "photo_0"}, {"image": "photo_1"}],
[{"image": "photo_2"}, {"image": "photo_3"}]
]
}
{{ reportage_photos }}
Pour un reportage photo multi-niveaux (ex : plusieurs
zones d'un chantier, chacune avec ses propres photos), placez le tag
{{ reportage_photos }} seul dans un paragraphe de votre
template. L'API genere automatiquement un tableau par niveau (3
photos par rangee, avec une ligne de description sous chaque rangee),
dans des blocs insecables (une rangee de photos ne sera jamais
coupee entre deux pages).
Fournissez vos donnees via la cle level_photo_groups
dans le JSON : une liste de groupes, chacun avec un
level_name (titre du niveau) et une liste
photos (chaque photo reference le nom du fichier
multipart via image, avec une liste optionnelle
non_conformities affichee sous la photo) :
{
"level_photo_groups": [
{
"level_name": "Niveau RDC",
"photos": [
{"image": "photo_0", "non_conformities": [
{"description": "Garde-corps absent", "norm_reference": "NF P01-012"}
]},
{"image": "photo_1"}
]
},
{
"level_name": "Niveau R+1",
"photos": [{"image": "photo_2"}]
}
]
}
Vous pouvez utiliser des tailles differentes pour chaque image. Exemple : photo panoramique large + photos detail petites :
# Photo panoramique {{ panorama.image | photo(160) }} # Photos detail {% for p in details %} {{ p.image | photo(60) }} {% endfor %}
Les signatures fonctionnent de la meme maniere. Envoyez-les comme
fichiers multipart et utilisez le filtre photo() :
# Dans le template Signature : {{ signature | photo(50) }} # curl — le nom du fichier DOIT commencer par "signature_" -F "signature_0=@signature.png" -F 'json_data={"signature": "signature_0"}'
Seuls les fichiers dont le nom commence par
signature_ (ex : signature_0,
signature_1) sont reconnus comme signature. Un champ
nomme simplement signature ne sera pas pris en
compte.
DocX propose aussi un service de signature electronique integre
(via Documenso), distinct du produit "Sign"
(sign.layerone.fr) mais accessible avec la
meme cle X-API-Key. Il est facture sur un quota
dedie (service_type="sign"), separe de votre quota de
generation de documents.
| Endpoint | Usage |
|---|---|
POST /create-signature-request |
Cree une demande de signature a partir d'un PDF (base64) et
d'une liste de signataires. Retourne un
document_id et les liens de signature
(signing_urls).
|
GET /signature-status/{document_id} |
Recupere le statut d'une demande de signature en cours. |
POST /detect-placeholders |
Utilitaire : detecte les emplacements de signature dans un PDF sans creer de demande — utile pour deboguer les positions avant l'envoi. |
GET /download-signed-pdf/{document_id} |
Telecharge le PDF signe (base64) une fois le document
COMPLETED.
|
DELETE /cancel-document/{document_id} |
Annule une demande de signature en cours et la supprime. |
Les filtres transforment vos donnees dans le template. Syntaxe :
{{ valeur | filtre }}
| Filtre | Usage | Resultat |
|---|---|---|
photo(l) / photo(l, h) |
{{ img | photo(80) }} |
Insere une image (l mm × h mm) |
maj |
{{ nom | maj }} |
MAJUSCULES |
min |
{{ nom | min }} |
minuscules |
cap |
{{ nom | cap }} |
Premiere lettre en majuscule |
titre |
{{ nom | titre }} |
Chaque Mot En Majuscule |
euro |
{{ montant | euro }} |
1 500,00 € |
suffix("...") |
{{ val | suffix(" kg") }} |
Ajoute un suffixe |
prefix("...") |
{{ val | prefix("N° ") }} |
Ajoute un prefixe |
date_fr |
{{ date_debut | date_fr }} |
Formate une date ISO (AAAA-MM-JJ ou ISO datetime)
en JJ/MM/AAAA. Chaine vide si non parsable (jamais
"N/A").
|
has_intervention_dates |
{% if interventions | has_intervention_dates %}
|
Filtre booleen : vrai si la liste contient au moins une intervention avec une date renseignee. Sert a conditionner l'affichage d'un tableau d'interventions. |
En cas d'erreur, la reponse contient un champ detail qui
explique le probleme.
| Code | Signification | Que faire ? |
|---|---|---|
| 200 | Tout va bien | Vous recevez le fichier. |
| 400 | Requete invalide | Verifiez les parametres envoyes. |
| 401 | Cle API manquante ou invalide | Verifiez le header X-API-Key. |
| 403 | Cle API valide mais pas autorisee pour ce service (ex : une cle du service signature utilisee sur un endpoint document). | Verifiez que votre cle correspond au bon service. |
| 404 | Endpoint ou ressource introuvable | Verifiez l'URL. |
| 413 | Requete trop volumineuse (limite de votre forfait) | Reduisez la taille du fichier ou passez au plan superieur. |
| 422 | Donnees invalides | JSON mal forme ou fichier manquant. |
| 429 |
Trop de requetes — 3 causes possibles :
quota mensuel atteint (la plus frequente, verifiez avec
/usage-stats), limite de requetes par minute
depassee, ou trop de requetes simultanees pour votre plan.
|
Verifiez votre quota. Si le quota est bon, attendez quelques secondes avant de reessayer. |
| 500 | Erreur serveur | Reessayez. Si ca persiste, contactez le support. |
| 503 | Service temporairement indisponible (base de donnees inaccessible) | Reessayez dans quelques instants. |
| 504 | Conversion PDF trop longue (timeout) | Simplifiez le template ou reessayez. |
# Exemple d'erreur {"detail": "Quota mensuel depasse. Passez au plan superieur."}