Quickstart
Generate a Word document from a DocDealer template in a single API call, with curl, Python or TypeScript.
One request creates a document from a template, fills the form, generates every section, and
returns a .docx. Use it when you want a finished document and don't need to hold state in
between.
Before you start
Mint an API key at /admin/api-keys (see Authentication). The plaintext is
shown once. Give it at least documents:write.
Generate in one call
POST /api/documents/generate is the whole flow. Pass a templateId and it creates the document
for you; pass a documentId instead to reuse one you already have.
curl -X POST https://thedocdealer.com/api/documents/generate \
-H "Authorization: Bearer $DOCDEALER_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "2359ce30-66f2-4f02-be82-d2231a2a5ec3",
"title": "Poder — Ana Torres",
"formData": {
"intro._count": 1,
"intro.0.fecha": "2026-08-24",
"intro.0.notario": "Salvador Farres Ripoll"
},
"validateForm": true,
"strict": true
}'The response is { documentId, blob, filename, warnings }, where blob is the DOCX
base64-encoded. Ask for "returnHtml": true and you get html instead, with no blob.
Getting formData right
This is where integrations usually stumble. Keys are flat and dotted:
{
"comparecientes._count": 2,
"comparecientes.0.nombre": "Ana Torres",
"comparecientes.1.nombre": "Luis Gil"
}The pattern is objectKey.entryIndex.fieldKey, plus one objectKey._count per repeating object
saying how many entries it has. Read the keys from a template's formSchema:
GET /api/templates/{templateId} returns objects and fields, and each field's fieldKey
(falling back to its id) is what goes in the key.
An unrecognised key fails the whole request, and the error names the valid patterns — worth reading rather than guessing.
Full example
Same flow in three languages — pick one.
import base64, json, os, urllib.request
API = "https://thedocdealer.com"
KEY = os.environ["DOCDEALER_API_KEY"]
def call(path, payload=None):
data = json.dumps(payload).encode() if payload is not None else None
req = urllib.request.Request(
f"{API}{path}",
data=data,
method="POST" if data else "GET",
headers={
"Authorization": f"Bearer {KEY}",
"Accept": "application/json",
**({"Content-Type": "application/json"} if data else {}),
},
)
with urllib.request.urlopen(req) as r:
return json.loads(r.read())
# 1. Find a template.
template = call("/api/templates")["templates"][0]
# 2. Read its form schema to learn the field keys.
schema = call(f"/api/templates/{template['id']}")["formSchema"]
# 3. Generate. One call creates the document, fills it, and renders the DOCX.
result = call("/api/documents/generate", {
"templateId": template["id"],
"title": "Poder — Ana Torres",
"formData": {"intro._count": 1, "intro.0.fecha": "2026-08-24"},
"validateForm": True,
"strict": True,
})
with open(result["filename"], "wb") as f:
f.write(base64.b64decode(result["blob"]))
print("wrote", result["filename"], "document", result["documentId"])Flags worth knowing
validateForm— runs form validation alongside generation and returnswarnings.form. Off by default.strict— fail the whole request if any phase fails, instead of returning a partial document. Off by default, except for ephemeral runs.ephemeral— run entirely in memory with no document row. RequirestemplateSourcerather than a stored template.generateTitle— defaults to true only when you omittitle.
Next
Need attachments, OCR, or step-by-step control instead of one shot? See Documents. Want your users editing in your own app? See Embedding.