API Reference

Generate beautiful, PDF/A-2A + PDF/UA-1 compliant documents from structured data. Designed for AI agents and API clients — no templates to learn, no design decisions to make.

Overview

The API is split into two stages — one creative, one mechanical:

EndpointPurposeSpeedRate limit
POST /api/v1/templatesCreate a reusable template (AI)~20s30/hour
GET /api/v1/templatesList your templatesfast
GET /api/v1/templates?id=...Get a single templatefast
POST /api/v1/renderRender PDF from template~100ms100/hour
POST /api/v1/renderOne-shot generate + render~20s50/hour
POST /api/v1/mdMarkdown to PDF~100ms200/hour
POST /api/v1/carouselMarkdown to carousel PDF~100ms200/hour

Recommended workflow: Create a template once, then render repeatedly with different data. This gives you consistent design, fast renders, and the ability to generate alternatives and pick the one you like.

Authentication

All endpoints require authentication. Two methods are supported:

  1. Bearer token — pass your API key in the Authorization header:
Authorization: Bearer your-api-key
  1. Session cookie — if you're logged in to the web app, your session cookie is accepted automatically.

Unauthenticated requests receive a 401 response.

Device Authorization (for AI agents & CLIs)

If your agent or CLI needs to authenticate without pre-configured API keys, use the OAuth Device Authorization Grant flow. The agent opens the user's browser, the user approves, and the agent receives a persistent API key.

  1. Request a device code POST /api/v1/device/code with an optional client_name. Returns device_code, user_code, verification_uri_complete, expires_in, and interval.
  2. Open the browser — direct the user to verification_uri_complete (code pre-filled) or display the user_code for manual entry.
  3. Poll for the token POST /api/v1/device/token with device_code every 5 seconds.
  4. Handle responses authorization_pending (keep polling), slow_down (increase interval by 5s), access_denied (abort), expired_token (restart).
  5. On success — the response includes an access_token. Store it and use as Authorization: Bearer <token> for all subsequent API calls. The token is a persistent API key.
# 1. Request device code curl -X POST https://makespdf.com/api/v1/device/code \ -H "Content-Type: application/json" \ -d '{"client_name": "My AI Agent"}' # 2. Open the verification_uri_complete in the user's browser # 3. Poll for the token curl -X POST https://makespdf.com/api/v1/device/token \ -H "Content-Type: application/json" \ -d '{"device_code": "<device_code from step 1>"}'

Endpoints

POST/api/v1/templates

Generate a reusable PDF template via AI. The template is a design tailored to your data shape, with {{variable}} placeholders that can be filled with different data on each render. Call multiple times with the same input to get alternative designs.

Request body
FieldTypeDescription
type requiredstringDocument type: "invoice", "receipt", "quote", "cv", "resume", "statement", "certificate", or "letter".
data requiredobjectThe structured data for your document. The AI designs the template around this shape. Max 50KB.
style optionalstringStyle hint: "modern", "classic", "minimal", "bold", or a freeform description. Max 200 chars.
brand optionalobjectBrand customisation with optional logoUrl, primaryColor, secondaryColor, and fontPreference.
Example request
POST /api/v1/templates Content-Type: application/json Authorization: Bearer your-api-key { "type": "invoice", "style": "modern", "brand": { "primaryColor": "#2563eb", "logoUrl": "https://example.com/logo.png" }, "data": { "company": "Acme Corp", "invoiceNumber": "INV-001", "date": "2025-03-15", "items": [ { "description": "Web design", "quantity": 1, "unitPrice": 2500 }, { "description": "Hosting (annual)", "quantity": 1, "unitPrice": 300 } ], "subtotal": 2800, "tax": 280, "total": 3080 } }
Example response
201 Created { "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "type": "invoice", "style": "modern", "model": "claude", "version": 1, "dataShape": "company,date,invoiceNumber,items[description,quantity,unitPrice],subtotal,tax,total", "generationMs": 18420, "costUsd": 0.032, "createdAt": "2025-03-15T10:30:00.000Z" }
Tip: The dataShape field describes the structure of your data. Templates work best when re-used with data of the same shape — same keys, same array structure.
GET/api/v1/templates

List your saved templates, newest first. Only returns templates owned by the authenticated caller.

Query parameters
FieldTypeDescription
id optionalstring (UUID)If provided, returns a single template by ID (including the full content/definition).
type optionalstringFilter by document type: "invoice", "receipt", "quote", "cv", "statement", "certificate", or "letter".
limit optionalnumberMax results to return. Default 20, max 100.
offset optionalnumberPagination offset. Default 0.
Example: list templates
GET /api/v1/templates?type=invoice&limit=5 Authorization: Bearer your-api-key
200 OK { "templates": [ { "templateId": "a1b2c3d4-...", "type": "invoice", "style": "modern", "dataShape": "company,date,items[...],total", "model": "claude", "version": 1, "createdAt": "2025-03-15T10:30:00" } ], "total": 1, "limit": 5, "offset": 0 }
Example: get single template
GET /api/v1/templates?id=a1b2c3d4-e5f6-7890-abcd-ef1234567890 Authorization: Bearer your-api-key

Returns the full template including the content field (the DocumentDefinition JSON). The list endpoint omits content to keep responses compact.

POST/api/v1/render

Render a PDF. Supports two modes depending on the request body:

Mode 1: Render from template (fast)

Pass a templateId from a previously created template, plus fresh data. No AI call — renders in ~100ms.

FieldTypeDescription
templateId requiredstring (UUID)The ID of a template created via POST /api/v1/templates.
data requiredobjectFresh data to populate the template. Should match the same shape as the original. Max 50KB.
Example request
POST /api/v1/render Content-Type: application/json Authorization: Bearer your-api-key { "templateId": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "data": { "company": "Different Corp", "invoiceNumber": "INV-002", "date": "2025-03-20", "items": [ { "description": "Consulting", "quantity": 10, "unitPrice": 150 } ], "subtotal": 1500, "tax": 150, "total": 1650 } }
Mode 2: One-shot render (slow)

Pass a type and data to generate a template and render in a single request. Convenient but slower (~20s) and the template is not saved for reuse.

FieldTypeDescription
type requiredstringDocument type: "invoice", "receipt", "quote", "cv", "resume", "statement", "certificate", or "letter".
data requiredobjectThe structured data for your document. Max 50KB.
style optionalstringStyle hint. Max 200 chars.
brand optionalobjectBrand customisation (logoUrl, primaryColor, secondaryColor, fontPreference).
Example request
POST /api/v1/render Content-Type: application/json Authorization: Bearer your-api-key { "type": "receipt", "data": { "store": "Corner Shop", "date": "2025-03-15", "items": [ { "name": "Coffee", "quantity": 2, "price": 4.50 }, { "name": "Croissant", "quantity": 1, "price": 3.00 } ], "total": 12.00, "paymentMethod": "Card ending 4242" } }
Response

By default, the response is the PDF binary with Content-Type: application/pdf. Metadata is returned in response headers:

FieldTypeDescription
X-Pages requiredheaderNumber of pages in the PDF.
X-Render-Ms requiredheaderTotal render time in milliseconds.
X-Model optionalheaderAI model used (only present for one-shot mode).
X-Rate-Limit-Remaining requiredheaderRemaining requests in the current rate limit window.
JSON metadata: Send Accept: application/json to receive render metadata as JSON instead of the PDF binary. Useful for checking page count and timing without downloading the file.
POST/api/v1/md

Convert GitHub Flavored Markdown to PDF. No AI call — pure computation, typically under 200ms.

Request body
FieldTypeDescription
markdown requiredstringGitHub Flavored Markdown content. Max 200KB.
options.pageSize optionalstring"A3", "A4" (default), "A5", "Letter", or "Legal".
options.fontFamily optionalstring"Inter" (default) or "NotoSans".
options.fontSize optionalnumberBase font size in points (6–24, default 10).
options.margins optionalarray[top, right, bottom, left] in points. Default [40, 40, 40, 40].
options.title optionalstringPDF document title. Max 200 chars.
Example request
POST /api/v1/md Content-Type: application/json Authorization: Bearer your-api-key { "markdown": "# Hello\n\nSome **bold** text.", "options": { "pageSize": "Letter", "fontFamily": "NotoSans" } }
POST/api/v1/carousel

Convert Markdown to a carousel-style PDF with one slide per page. Optimized for LinkedIn document uploads. Slides are separated by horizontal rules (---). The first slide is a title slide with larger headings and vertical centering. No AI call — pure computation, typically under 200ms.

Request body
FieldTypeDescription
markdown requiredstringMarkdown content with --- as slide separators. Requires blank lines before and after ---. Max 200KB.
options.format optionalstring"square" (default, 1080×1080px) or "portrait" (1080×1350px).
options.theme optionalstring"light" (default) or "dark".
options.fontFamily optionalstring"Inter" (default) or "NotoSans".
options.fontSize optionalnumberBase font size in points (12–36, default 20).
options.title optionalstringPDF document title. Max 200 chars.
Example request
POST /api/v1/carousel Content-Type: application/json Authorization: Bearer your-api-key { "markdown": "# My Talk\nSubtitle here\n\n---\n\n## Key Insight\nOne idea per slide keeps your audience engaged.\n\n---\n\n## Thanks!\nFollow me for more.", "options": { "format": "square", "theme": "dark" } }
Slide separators: Use --- with blank lines above and below to create slide breaks. Without blank lines, --- may be parsed as a setext heading instead.

Data formatting

Dates and monetary values in your data are automatically pre-formatted before rendering:

  • Dates: 2025-03-15 becomes "15 March 2025". 2025-03 becomes "March 2025".
  • Money: 12250 becomes "12,250.00" (for fields named amount, price, total, tax, etc.)

You can send raw values — the API handles the formatting. Currency symbols should be included in your data if needed (e.g. a currency field).

Supported document types

TypeDescriptionTypical data
invoiceBusiness invoicesLine items, totals, payment details, addresses
receiptTransaction confirmationsItems, totals, payment method, store info
quoteQuotes and estimatesItemised pricing, validity, terms
cv / resumeCVs and resumesExperience, education, skills, contact info
statementAccount statementsTransactions, balances, date ranges
certificateCertificatesRecipient, issuer, date, details
letterFormal lettersSender, recipient, subject, body paragraphs

Errors

All errors return JSON with an error field:

{ "error": "Invalid request", "details": ["type: Required"] }
StatusMeaning
400Invalid request body. Check the details array for field-level errors.
404Template not found (wrong ID or not owned by your API key).
429Rate limit exceeded. Check the Retry-After header for seconds until reset.
500Internal error (AI failure, rendering error, etc.)
503Service unavailable — no AI providers configured.