Appearance
API Reference
We expose a public render endpoint and template CRUD endpoints.
For fully programmatic onboarding, see Agent Onboarding.
Authentication
All API calls require an API key:
X-API-Key: <your_key>Agent Signup
You can create an account and receive an API key directly:
json
{
"name": "Agent User",
"email": "agent-user@example.com",
"password": "replace-with-strong-password",
"mode": "agent",
"attribution": {
"first_touch_channel": "website:pdfmancer_landing",
"utm_source": "pdfmancer_landing",
"utm_medium": "website",
"utm_campaign": "landing_core",
"utm_content": "hero_signup",
"utm_term": "json to pdf api",
"landing_locale": "en",
"referrer_path": "/en"
}
}Endpoint:
POST https://dashboard.pdfmancer.com/api/auth/signup
Attribution fields are optional and allow campaign tracking for lead generation.
Schema Discovery
GET /api/public/schema/current
Returns schema metadata and current version.
json
{
"schema_name": "document",
"current_version": "v1",
"schema_url": "/api/public/schemas/document/v1.json"
}GET /api/public/schemas/document/{version}.json
Returns the versioned JSON Schema used by the API validator.
Example:
GET /api/public/schemas/document/v1.json
Validate
POST /api/public/validate
Runs schema validation only and returns actionable issues for agents.
json
{
"schema_version": "v1",
"template": {
"elements": [{ "type": "text", "value": "Hello {{ customer.name }}" }]
},
"data": {
"customer": { "name": "ACME" }
}
}Response shape:
json
{
"ok": true,
"schema_version": "v1",
"issues": [],
"summary": { "error_count": 0, "warning_count": 0 }
}Issue shape:
code: stable machine code (for exampleSCHEMA_REQUIRED).phase:templateorresolved_template.path: JSON pointer path.message: raw validation error.suggestion: remediation hint for agents.severity:errororwarning.
Templating-related issue codes:
PLACEHOLDER_UNSAFE_EXPRESSIONPLACEHOLDER_FILTER_INVALIDPLACEHOLDER_MISSING(warning)
Render
POST /api/public/render
Render either a raw template or a saved template with data.
Supported element types: text, heading, divider, list, image, row, table, spacer, pagebreak.
Raw template
json
{
"elements": [
{ "type": "heading", "value": "Invoice {{ invoice.number }}", "level": 2 },
{ "type": "divider" },
{ "type": "text", "value": "Hello {{ customer.name }}" }
]
}Dynamic table (loop alias item)
json
{
"elements": [
{
"type": "table",
"source": "lines",
"columns": [
{ "label": "Concept", "value": "{{ item.concept }}" },
{ "label": "Qty", "value": "{{ item.quantity }}" },
{ "label": "Total", "value": "{{ item.line_total }}", "format": "currency" }
],
"empty_message": "No lines"
}
]
}Static table (data matrix)
json
{
"elements": [
{
"type": "table",
"data": [
["Item", "Qty", "Price"],
["Widget A", 2, 19.99],
["Cable B", 1, 4.5]
]
}
]
}Saved template
json
{
"template_id": "<template_id>",
"data": {
"customer": { "name": "ACME" },
"total": 123.45
}
}You can also pass schema_version in the wrapped payload (template or template_id) to pin validation version. If validation fails, render returns 400 with the same actionable issues[] structure as /api/public/validate. Template placeholders use a strict safe subset (see Templating); unsupported expressions are rejected. In render, missing placeholder data is replaced with empty string.
Async Render Jobs (AI-first)
POST /api/public/render-jobs
Create an async JSON-to-PDF job. This route is designed for paste JSON -> get PDF UX. If input_json is already a valid document template ({ "elements": [...] }), the job skips AI planning and renders that template directly.
json
{
"input_json": {
"title": "Article list",
"items": [
{ "name": "Widget A", "qty": 2, "price": 19.99 }
]
},
"options": {
"allow_raw_for_ai": false,
"max_table_rows": 40,
"user_prompt": "Treat this payload as an invoice and keep totals prominent."
}
}Response (202):
json
{
"job_id": "job_123",
"status": "queued"
}GET /api/public/render-jobs/
Read job status and generated artifacts.
Typical consumer flow:
- Create job with
POST /api/public/render-jobs. - Poll status until
successorerror. - Download file from
/filewhen status issuccess.
Status values:
queuedprocessingsuccesserror
Example response:
json
{
"job_id": "job_123",
"status": "success",
"progress_stage": "success",
"template_json": {
"elements": [
{ "type": "heading", "value": "Article list", "level": 2 }
]
},
"document_plan": {
"doc_type": "generic"
},
"planning_trace": {
"strategy": "ai",
"provider": "openrouter",
"model": "openai/gpt-4o-mini",
"attempts": 2,
"anonymized": true,
"detected_doc_type": "invoice_basic",
"doc_type_score": 0.91,
"matched_signals": ["explicit_doc_kind", "line_items", "totals"],
"user_prompt_used": true,
"language_selected": "es",
"validation": {
"schema_valid": true,
"repaired": true
}
}
}GET /api/public/render-jobs/{job_id}/file
Returns the generated PDF (application/pdf) when job status is success. If job is still running, returns 409.
Async job caveats (consumer-facing)
input_jsonmust be a JSON object or array.- Processing time depends on payload size and structure complexity.
- Planning input is anonymized by default unless
allow_raw_for_ai=true. - You can add
options.user_prompt(up to 1200 chars) to steer the planner for specific layout goals. - If status is
error, inspecterroranderror_codefrom the status response for recovery.
Templates
POST /api/templates
json
{
"name": "Invoice",
"description": "Basic invoice",
"template": {
"elements": [
{ "type": "text", "value": "Invoice {{ invoice.number }}" }
]
}
}GET /api/templates
Returns templates for your team.
GET /api/templates/{id}
Returns a single template.
PUT /api/templates/{id}
json
{
"name": "Invoice v2",
"template": {
"elements": [
{ "type": "text", "value": "Updated" }
]
}
}DELETE /api/templates/{id}
Deletes a template.
Use the interactive tester in Live Explorer.