Developer Reference
Everything you need to integrate DocuForge into your stack — authentication, endpoints, webhooks, and error handling.
Quick Start
Generate your first document in under 3 minutes.
Create your API key in the dashboard
Navigate to Dashboard → API Keys → Create Key. Choose sk_test_ for staging or sk_live_ for production.
Generate a document via POST
curl -X POST https://api.docuforge.io/api/v1/documents/generate \
-H "Authorization: Bearer sk_test_YOUR_KEY" \
-H "Content-Type: application/json" \
-d '{
"templateId": "tmpl_abc123",
"data": {
"recipient": "Marie Dupont",
"amount": "4 500,00 €",
"date": "2026-01-15"
}
}'Receive your document ID and download link
{
"correlationId": "doc_9f3a7bc1d2e4",
"status": "RENDERED",
"templateId": "tmpl_abc123",
"createdAt": "2026-01-15T09:42:11Z",
"expiresAt": null,
"downloadUrl": "/api/v1/documents/doc_9f3a7bc1d2e4/download",
"verifyUrl": "/verify/qr_tk_xxxx",
"contentHash": "sha256:4b2d..."
}Authentication
DocuForge supports two authentication methods depending on context.
API Key (Bearer Token)
Use your sk_test_* or sk_live_* key in every request header. Scoped to your tenant with configurable expiration and IP restrictions.
Authorization: Bearer sk_live_YOUR_SECRET_KEY
JWT — Dashboard Sessions
Short-lived RS256 JWT tokens issued upon sign-in for the dashboard. Not recommended for server-to-server integrations. Rotate every 1 hour automatically.
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...
API Key Scopes
Documents API
Core endpoints for document generation, retrieval, lifecycle management, and verification.
/api/v1/documents/generate
Request body schema:
{
"templateId": "string (required) — UUID of the target template",
"data": "object (required) — Variable bindings for the template",
"expiresAt": "string (optional) — ISO-8601 expiry datetime",
"webhookUrl": "string (optional) — Override default webhook for this request",
"metadata": "object (optional) — Arbitrary key/value stored with the document"
}Templates API
Manage your document templates — HTML, PDF-fill, or DOCX sources with versioning and checksums.
Template Object
{
"id": "tmpl_abc123",
"name": "Bulletin de paie FR",
"type": "HTML_TO_PDF",
"version": 3,
"checksum": "sha256:f3a2...",
"variables": ["recipient", "amount", "date", "company"],
"createdAt": "2025-11-01T08:00:00Z",
"updatedAt": "2026-01-10T14:22:00Z"
}Webhooks
Receive real-time event notifications delivered to your endpoint via HTTPS POST.
Webhook Payload Example
{
"event": "document.generated",
"timestamp": "2026-01-15T09:42:13Z",
"tenantId": "ten_xk99z",
"data": {
"correlationId": "doc_9f3a7bc1d2e4",
"templateId": "tmpl_abc123",
"status": "RENDERED",
"downloadUrl": "/api/v1/documents/doc_9f3a7bc1d2e4/download"
},
"signature": "sha256=hmac_XXXX"
}X-DocuForge-Signature header using HMAC-SHA256 with your webhook secret before processing the payload.Error Codes
All errors follow a consistent JSON structure. Use request_id when contacting support.
{
"status": 422,
"error": "UNPROCESSABLE_ENTITY",
"message": "Template variable 'amount' is required but was not provided",
"request_id": "req_7f2a1b9c",
"timestamp": "2026-01-15T09:42:11Z"
}Pipeline Editor
The Pipeline Editor lets you define, on each template, a complete data orchestration graph that runs before rendering. It fetches data from external sources, transforms it, validates it, generates values, and assembles the final payload — all without writing backend code.
Full pipeline_config structure
{
"version": "2.0",
"name": "Insurance Policy Generation",
"description": "Fetch policy, validate, generate reference, render",
// Execution mode
"mode": "SINGLE", // SINGLE | BATCH | FAN_OUT
"output": "SINGLE_PDF", // SINGLE_PDF | ZIP | MERGED_PDF | INDIVIDUAL
// Global variables injected into every step context
"variables": {
"base_url": "https://api.internal.com",
"tax_rate": "0.20"
},
// Static constants accessible as {{constants.name}}
"constants": {
"company_name": "Atlantis Finance",
"footer_text": "Document generated by DocuForge"
},
// Execution limits
"timeout_ms": 120000,
"max_retries": 2,
// Steps — executed in order unless CONDITION branches
"steps": [ /* see Step Types below */ ],
// Optional: overlay text/images on the final PDF
"post_processing": [ /* see Post-Processors below */ ],
// Error policy
"error_handling": {
"strategy": "FAIL_FAST",
"notify_on_error": true,
"error_webhook_url": "https://your-system.com/hooks/docuforge-errors"
}
}Step Types — all 15
Call your organisation's configured gateway (insurance core, ERP…). Authentication is handled automatically by the configured gateway.
{
"id": "fetch_policy",
"type": "GATEWAY_CALL",
"description": "Fetch policy details from core system",
"gateway_config_id": 1,
"method": "GET",
"endpoint": "/policies/{{input.policy_number}}",
"output_key": "policy"
}Authenticate against an OAuth2 or token endpoint. The extracted token is stored and automatically injected by EXTERNAL_CALL steps that reference this step.
{
"id": "auth_step",
"type": "EXTERNAL_AUTH",
"url": "https://partner-api.com/oauth/token",
"method": "POST",
"body": {
"grant_type": "client_credentials",
"client_id": "{{vars.client_id}}",
"client_secret": "{{vars.client_secret}}"
},
"extract": {
"token": "$.access_token",
"expires_in": "$.expires_in"
},
"output_key": "auth"
}HTTP call to an external service. If an EXTERNAL_AUTH step ran before, its token is injected automatically in the Authorization header.
{
"id": "fetch_client",
"type": "EXTERNAL_CALL",
"url": "https://partner-api.com/clients/{{input.client_id}}",
"method": "GET",
"headers": {
"X-Custom-Header": "docuforge"
},
"output_key": "client",
"continue_on_error": true,
"default_on_error": { "name": "Unknown", "email": "" }
}Raw HTTP call without gateway or auth context. Use for public endpoints or when you manage auth manually via headers.
{
"id": "get_rates",
"type": "HTTP_CALL",
"url": "https://api.ecb.europa.eu/stats/rates?currency=EUR",
"method": "GET",
"response_extract": "$.rates[0].value",
"output_key": "eur_rate",
"timeout_ms": 5000
}Conditional branching. Evaluates an expression and routes execution to then_steps or else_steps. Skipped branch steps are completely excluded from context.
{
"id": "check_vip",
"type": "CONDITION",
"condition": "{{client.category}} == 'VIP'",
"then_steps": ["fetch_vip_discount", "apply_premium_template"],
"else_steps": ["apply_standard_template"],
"on_true": "CONTINUE",
"on_false": "CONTINUE"
}Data transformation step. Supports 14 operations: CONCAT, UPPERCASE, LOWERCASE, TRIM, FORMAT_DATE, TEMPLATE, CALCULATE, SUBSTRING, REPLACE, DEFAULT, LENGTH, NOW, TODAY.
{
"id": "format_data",
"type": "TRANSFORM",
"transformations": [
{
"target": "full_name",
"operation": "CONCAT",
"sources": ["client.first_name", "client.last_name"],
"pattern": " "
},
{
"target": "effective_date",
"operation": "FORMAT_DATE",
"sources": ["policy.start_date"],
"pattern": "dd/MM/yyyy"
},
{
"target": "total_ttc",
"operation": "CALCULATE",
"sources": ["policy.premium"],
"pattern": "{{policy.premium}} * 1.20"
},
{
"target": "ref_label",
"operation": "TEMPLATE",
"pattern": "Police N° {{policy.number}} — {{full_name}}"
},
{
"target": "status_upper",
"operation": "UPPERCASE",
"sources": ["policy.status"]
}
],
"output_key": "formatted"
}Generate dynamic values: unique IDs, formatted dates, sequential references, random tokens. Useful for document numbers and tracking references.
{
"id": "gen_ref",
"type": "GENERATE",
"generate_type": "REFERENCE_NUMBER",
"format_pattern": "ATT-{YEAR}-{ORG}-{SEQ:8}",
"output_key": "doc_reference"
}
// Other generate_type values:
// "UUID" → "f47ac10b-58cc-4372-a567-0e02b2c3d479"
// "UUID_SHORT" → "f47ac10b"
// "SEQUENCE" → "00000042" (format_pattern: "%08d")
// "DATE" → "2026-02-28" (format_pattern: "yyyy-MM-dd")
// "DATETIME" → "2026-02-28T12:00:00Z"
// "YEAR" → "2026"
// "RANDOM" → random alphanumeric stringMerge results from multiple steps into one object. Supports DEEP_MERGE, SHALLOW_MERGE, OVERRIDE, ARRAY_CONCAT, CHERRY_PICK strategies. Use field_mapping for selective cherry-picking.
{
"id": "assemble",
"type": "MERGE",
"sources": ["policy", "client", "formatted", "gen_ref"],
"merge_strategy": "DEEP_MERGE",
"conflict_resolution": "LAST_WINS",
"field_mapping": {
"holder_name": "{{client.full_name}}",
"policy_number": "$.policy.reference",
"document_ref": "{{gen_ref}}",
"premium_ttc": "{{formatted.total_ttc}}",
"effective_date": "{{formatted.effective_date}}"
},
"output_key": "final_payload"
}Validate required fields before rendering. Supports REQUIRED, REGEX, MIN_LENGTH, MAX_LENGTH, ENUM, RANGE rules. By default, a failed validation stops the pipeline.
{
"id": "validate_payload",
"type": "VALIDATION",
"validation_rules": [
{ "field": "client.email", "rule": "REGEX", "value": "^[^@]+@[^@]+$", "message": "Invalid email format" },
{ "field": "policy.premium", "rule": "RANGE", "value": "0,999999", "message": "Premium out of range" },
{ "field": "policy.type", "rule": "ENUM", "value": "AUTO,HABITATION,VIE", "message": "Unknown policy type" },
{ "field": "client.last_name", "rule": "MIN_LENGTH","value": "2", "message": "Name too short" },
{ "field": "policy.number", "rule": "REQUIRED", "value": "", "message": "Policy number is mandatory" }
]
}Fine-grained field-level mapping from one context path to another. Lighter than MERGE — use when you just need to reshape a few fields.
{
"id": "remap",
"type": "FIELD_MAPPING",
"field_mapping": {
"insured_name": "$.client.full_name",
"policy_ref": "$.policy.number",
"coverage_amount": "$.policy.coverage.total"
},
"output_key": "mapped"
}Evaluate a SpEL (Spring Expression Language) expression for complex business logic not covered by TRANSFORM. Has access to the full context.
{
"id": "compute_risk",
"type": "SCRIPT",
"script_language": "spel",
"expression": "T(Math).round(#policy.premium * #client.risk_factor * 100.0) / 100.0",
"output_key": "adjusted_premium"
}Execute another template's pipeline as a sub-routine. Perfect for reusable data enrichment flows (e.g., a "fetch-client" pipeline shared across many templates).
{
"id": "run_kyc",
"type": "SUB_PIPELINE",
"sub_pipeline_template_code": "KYC_ENRICHMENT",
"sub_pipeline_input": {
"client_id": "{{input.client_id}}",
"check_level": "FULL"
},
"output_key": "kyc_result"
}Final step to call an external rendering service and retrieve the document as base64 or a URL link. Used when rendering is delegated to a third-party (e.g., partner document system).
{
"id": "external_render",
"type": "POST_EXTERNAL_RESULT",
"url": "https://render.partner.com/generate",
"method": "POST",
"body_template": { "data": "{{final_payload}}", "format": "PDF" },
"response_mode": "BUFFER_BASE64",
"buffer_base64_path": "$.document.content",
"content_type_path": "$.document.mime_type",
"file_name_path": "$.document.name",
"output_key": "external_result"
}Read or write from Redis cache. Set cache_ttl_seconds to write. Omit it to read. Avoids redundant external calls for stable data (e.g., reference tables, exchange rates).
// Write to cache
{
"id": "cache_rates",
"type": "CACHE",
"cache_key": "exchange_rates_eur",
"cache_ttl_seconds": 3600,
"sources": ["eur_rate"]
}
// Read from cache
{
"id": "read_rates",
"type": "CACHE",
"cache_key": "exchange_rates_eur",
"output_key": "cached_rates",
"continue_on_error": true
}Log the current pipeline context to the application logs. Only active in staging environment (sk_test_ key). No-op in production.
{
"id": "debug",
"type": "DEBUG_LOG",
"description": "Inspect context after merge"
}HTML → PDF Templates
Write standard HTML/CSS. DocuForge resolves placeholders, executes loops, computes aggregations and arithmetic, then converts to a pixel-perfect PDF via Flying Saucer + iText.
Basic placeholders
<!-- Direct field access -->
<p>Dear {{client.last_name}},</p>
<!-- Nested object -->
<p>Policy: {{policy.details.reference}}</p>
<!-- Global variable (defined in pipeline.variables) -->
<p>Issued by: {{vars.company_name}}</p>
<!-- Current item inside a loop (use dot notation) -->
{{#items}}
<p>{{.name}} — {{.quantity}}</p>
{{/items}}Loop blocks — repeat a section for each array item
<!-- Loop over invoice lines -->
<table>
<thead>
<tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr>
</thead>
<tbody>
{{#invoice.lines}}
<tr>
<td>{{description}}</td>
<td>{{quantity}}</td>
<td>{{unit_price}} €</td>
<td>{{total}} €</td>
</tr>
{{/invoice.lines}}
</tbody>
</table>
<!-- Nested loops (two levels) -->
{{#sections}}
<h2>{{section_title}}</h2>
{{#clauses}}
<p>{{clause_number}}. {{text}}</p>
{{/clauses}}
{{/sections}}Aggregation functions — compute totals directly in the template
<!-- Sum all "total" values in invoice.lines -->
<strong>Total HT: {{sum:invoice.lines.total}} €</strong>
<!-- Count items -->
<p>Number of lines: {{count:invoice.lines}}</p>
<!-- Average, min, max -->
<p>Average premium: {{avg:policies.premium}} €</p>
<p>Lowest: {{min:policies.premium}} €</p>
<p>Highest: {{max:policies.premium}} €</p>Inline arithmetic — {{calc: expression | decimal_places}}
<!-- Multiply qty × unit price, 2 decimal places -->
<td>{{calc: quantity * unit_price | 2}} €</td>
<!-- Apply tax rate -->
<td>{{calc: premium * 1.20 | 2}} €</td>
<!-- No decimal rounding -->
<td>{{calc: (price - discount) * qty}}</td>
<!-- Round to integer -->
<td>{{calc: total * 1.20 | 0}} €</td>DOCX Templates
Upload a .docx file. DocuForge resolves placeholders in paragraphs and table cells, expands loop blocks inside tables, and can embed images from the asset library. The output is a .docx file (or PDF after conversion).
Placeholder syntax in paragraphs
In any paragraph or text run:
{{client.full_name}} → resolved from enriched payload
{{policy.number}}
{{vars.company_name}} → global variable from pipeline
Nested path:
{{policy.holder.address.city}}Table loop — repeat rows for each array item
Table structure in the .docx file:
┌─────────────────┬──────────┬────────────┐
│ {{#items}} │ │ │ ← marker row (first column)
├─────────────────┼──────────┼────────────┤
│ {{description}} │ {{qty}} │ {{price}} │ ← template row (cloned per item)
├─────────────────┼──────────┼────────────┤
│ {{/items}} │ │ │ ← end marker row
└─────────────────┴──────────┴────────────┘
Rules:
• Start marker {{#array.path}} must be in the FIRST column of a row
• End marker {{/array.path}} must be in the FIRST column of a row
• All rows between markers are cloned for each item
• Inside the loop, use {{field}}, {{nested.field}}, or {{.}} for the item itself
• Marker rows are removed from the final documentAsset image insertion
In a paragraph (alone in its own paragraph):
{{assets.company_logo}}
Rules:
• The placeholder must be the ONLY content in its paragraph
• DocuForge detects {{assets.<name>}} and replaces the paragraph with the image
• The asset must be pre-uploaded in Dashboard → Assets
• Provide asset name as stored in the asset library
• Supported: PNG, JPG, GIF (converted to embedded image in docx)XLSX Templates
Upload a .xlsx file. DocuForge fills cell values and expands loop blocks — both horizontal (same row) and vertical (rows stacked downward).
Vertical loop — expand rows downward
Sheet layout:
Row 5 │ {{#invoice.lines}} │ │ │
Row 6 │ {{description}} │ {{qty}} │ {{price}} │ ← template row
Row 7 │ {{/invoice.lines}} │ │ │
Result for 3 items: rows 5–7 expand to rows 5–7 (original 3 template rows → 3 × n rows).
Marker rows are removed. Subsequent sheet content is shifted down automatically.Horizontal loop — expand columns rightward
Row 3: │ {{#months}} │ {{name}} │ {{/months}} │
Result: each month becomes a new column group.
Use for pivot-style reports where items become columns.Plain cell substitution (no loop)
Any cell can contain:
{{client.name}} → resolved to string value
{{policy.start_date}} → resolved, keeps cell formatting
{{vars.report_title}} → global variablePDF Form Fill
Upload a PDF with AcroForm fields. DocuForge maps data fields to form fields by name, fills them, and optionally flattens the result (making it non-editable). Use the rendering_config to define the mapping.
Field mapping — object format
{
"fields": {
"pdf_field_name_1": "client.full_name",
"pdf_field_name_2": "policy.number",
"pdf_field_date": "formatted.effective_date",
"pdf_field_amount": "formatted.total_ttc"
},
"flatten": true
}Field mapping — array format (explicit order)
{
"fields": [
{ "name": "ClientName", "path": "client.full_name" },
{ "name": "PolicyNumber", "path": "policy.number" },
{ "name": "StartDate", "path": "formatted.effective_date" },
{ "name": "PremiumAmount", "path": "formatted.total_ttc" }
],
"flatten": true,
"image_fields": [
{
"name": "SignatureBox",
"source": "client.signature_base64"
}
]
}Batch & ZIP
Generate one document per item in an array with a single API call. DocuForge runs the pipeline in FAN_OUT mode — each item gets its own enriched context — then aggregates all outputs into a ZIP archive or a merged PDF.
FAN_OUT pipeline config
{
"version": "2.0",
"mode": "FAN_OUT",
"output": "ZIP",
// Path to the array that drives iteration
"fan_out_on": "$.input.insured_persons",
// Max parallel executions (default: 5)
"fan_out_parallelism": 10,
// Naming template for each file inside the ZIP
"file_naming_template": "attestation-{index}-{item.last_name}",
"steps": [
{
// run_once=false → executes for EACH item (default)
"id": "enrich_person",
"type": "GATEWAY_CALL",
"endpoint": "/persons/{{current_item.id}}",
"output_key": "person_data"
},
{
// run_once=true → executes ONCE before fan-out starts
"id": "fetch_company_info",
"type": "HTTP_CALL",
"url": "https://api.internal.com/company",
"run_once": true,
"output_key": "company"
}
]
}Output modes
ZIP archive structure & batch metadata
batch-documents.zip
├── attestation-0-Martin.pdf
├── attestation-1-Dupont.pdf
├── attestation-2-Bernard.pdf
└── links.txt ← only if some documents are served as URLs
// Batch metadata in the pipeline result:
{
"batch_meta": {
"mode": "FAN_OUT",
"output": "ZIP",
"items_total": 3,
"items_success": 3,
"items_failed": 0
},
"external_result": {
"response_mode": "BUFFER_BASE64",
"document_base64": "<base64-zip-content>",
"content_type": "application/zip",
"file_name": "batch-documents.zip"
}
}Context variables inside a FAN_OUT loop
Inside any for_each step or template, these variables are available:
{{current_item}} → the current array element (full object)
{{current_item.field}} → specific field of the current item
{{current_index}} → 0-based index in the array
{{company}} → result of run_once=true steps (shared across all items)
Example in a template:
<p>Person {{current_index}} of {{count:insured_persons}}</p>
<p>Name: {{current_item.last_name}}, {{current_item.first_name}}</p>Post-Processors
Post-processors are applied to the final PDF after rendering. They overlay QR codes, barcodes, stamps, signatures, text, or page numbers. Each one can be conditionally applied at runtime.
Inject a QR code at a specific position. Source can be a URL, a document reference, or any data field.
{ "type": "QR_CODE", "source": "{{document.verification_url}}", "page": 1, "x": 450, "y": 700, "size": 80 }Inject a barcode (CODE_128, EAN_13, EAN_8, QR_CODE…). Useful for inventory or tracking documents.
{ "type": "BARCODE", "source": "{{policy.barcode_value}}", "barcodeFormat": "CODE_128", "page": 1, "x": 40, "y": 760, "width": 200, "height": 40 }Overlay an electronic signature image (base64 or URL) at a precise position.
{ "type": "SIGNATURE", "imageUrl": "{{client.signature_url}}", "page": -1, "x": 380, "y": 100, "width": 160, "height": 60 }Apply a stamp or seal image (approval stamp, company seal…). Supports opacity and rotation.
{ "type": "STAMP", "imageBase64": "{{vars.approval_stamp}}", "page": 1, "x": 300, "y": 400, "width": 120, "height": 120, "opacity": 0.6, "rotation": -15.0 }Place any image (logo, photo, icon) anywhere on any page.
{ "type": "IMAGE_OVERLAY", "imageUrl": "https://cdn.company.com/logo.png", "page": 0, "x": 40, "y": 20, "width": 100, "height": 40 }Overlay dynamic text (generation date, document number, "CONFIDENTIEL"…). Supports font, size, color, rotation.
{ "type": "TEXT_OVERLAY", "text": "Generated {{vars.today}} — Ref: {{doc_reference}}", "page": 0, "x": 40, "y": 800, "fontSize": 8, "color": "#888888" }Custom watermark text diagonal across pages. Distinct from the automatic staging watermark.
{ "type": "CUSTOM_WATERMARK", "text": "DRAFT — NOT VALID", "opacity": 0.12, "fontSize": 52, "color": "#FF0000", "rotation": -45 }Add page numbers to every page. Format: "Page {n} of {total}".
{ "type": "PAGE_NUMBERING", "text": "Page {n} / {total}", "x": 500, "y": 20, "fontSize": 9, "color": "#666666" }Add a fixed header and/or footer to every page.
{ "type": "HEADER_FOOTER", "text": "CONFIDENTIEL — {{vars.company_name}}", "page": 0, "x": 40, "y": 820, "fontSize": 8 }Password-protect the PDF and restrict permissions (printing, copying, editing).
{ "type": "PDF_ENCRYPTION", "password": "{{client.dob_as_password}}", "permissions": "PRINT" }Conditional post-processors
Every post-processor supports a condition field. If the expression evaluates to false, that post-processor is silently skipped.
// Only apply "DRAFT" watermark if the document is not yet validated
{ "type": "CUSTOM_WATERMARK", "text": "DRAFT", "condition": "{{policy.status}} != 'VALIDATED'" }
// Only add signature overlay if client has uploaded one
{ "type": "SIGNATURE", "imageUrl": "{{client.signature_url}}", "condition": "{{client.signature_url}} != null" }
// Only stamp on the last page
{ "type": "STAMP", "imageBase64": "{{vars.seal}}", "page": -1, "condition": "{{policy.approved}} == true" }Page reference values
Assets
Assets are reusable binary files (logos, stamps, signatures, fonts…) uploaded once in Dashboard → Assets and referenced from any template or post-processor. DocuForge injects them at render time.
Reference an asset in a DOCX template
In a DOCX paragraph (alone in its own paragraph):
{{assets.company_logo}}
{{assets.approval_stamp}}
{{assets.client_signature}}Reference an asset in an HTML template
<!-- DocuForge resolves assets.xxx to a data URI at render time -->
<img src="{{assets.company_logo}}" width="150" height="60" alt="Logo" />
<!-- Inside a loop (same syntax) -->
{{#signatories}}
<img src="{{assets.signature_{{role}}}}" />
{{/signatories}}Reference an asset in a post-processor
// In post_processing array:
{
"type": "STAMP",
"imageBase64": "{{assets.company_seal}}",
"page": -1,
"x": 380, "y": 80, "width": 100, "height": 100,
"opacity": 0.7
}Inject an asset dynamically from the pipeline
// In a TRANSFORM step, fetch and inject an asset base64 into context:
{
"id": "load_signature",
"type": "GATEWAY_CALL",
"endpoint": "/signatories/{{input.signatory_id}}/signature",
"output_key": "signatory_sig"
}
// Then in post_processing:
{
"type": "SIGNATURE",
"imageBase64": "{{signatory_sig.base64}}",
"condition": "{{signatory_sig}} != null",
"page": -1, "x": 400, "y": 120, "width": 150, "height": 60
}business_key — automatic document supersession
Configure business_key_field in the template metadata to automatically supersede the previous active version when a new one is generated.
// Template metadata (set in Dashboard → Template → Edit Metadata):
{
"business_key_field": "policy_number"
}
// When DocuForge generates a new document, it reads payload.policy_number,
// finds any ACTIVE document with the same key for the same organisation,
// and marks it SUPERSEDED automatically — no extra API call needed.
// You can also pass businessKey explicitly in the generation request:
{
"templateCode": "AVENANT_AUTO",
"payload": { "policy_number": "POL-2024-00123", ... },
"businessKey": "POL-2024-00123" // overrides template metadata if present
}Start integrating in minutes
Create your free developer account, generate a staging API key, and send your first document generation request before lunch.