Integrate makesPDF into your app

Drop-in “AI builds PDF templates in my app” without rebuilding the authoring loop. Three copy-paste recipes, each under 60 seconds. Everything below couples over plain REST with your own API key — bring your own LLM key, pay us only for the pixels.

Which recipe do you need?

You want…UseLives
Production PDFs from your backend with data you already haveRecipe 1: backend renderThis page
Your existing agent (Claude Code, Cursor, Desktop) authoring templates with schema-validated tools, zero codeRecipe 2: hosted MCPThis page + MCP server
Authoring tools embedded in your own app next to your own model callsRecipe 3: Vercel AI SDK tools (REST shim)Paste today; ships as @makespdf/sdk next

A prebuilt chat widget (<MakespdfChat>) is deferred until integrators ask for it — the three recipes above cover the backend, agent, and in-app cases without one. Full backend detail (error table, retries, versioning, delivery patterns) lives in the application-integration skill.

Recipe 1: render production PDFs from your backend~60 seconds

Save the template once, render it many times with different data. Billing is 1 credit per 10 pages on success; every failure path deducts zero. Rate limit: 2,000 renders/hour per caller.

Step 1: save the template (one time)

bash
# Write your DSL to a file, then save it and capture the templateId
jq -n --rawfile dsl template.js --arg name "Invoice v1" \
  '{ dsl: $dsl, name: $name }' |
  curl -sX POST https://makespdf.com/api/v1/templates \
    -H "Authorization: Bearer $MAKESPDF_API_KEY" \
    -H "Content-Type: application/json" \
    --data-binary @-
# -> { "templateId": "11111111-2222-...", "name": "Invoice v1", "createdAt": ... }

# Store it as config, e.g. MAKESPDF_INVOICE_TEMPLATE_ID=11111111-2222-...

Step 2: render with real data (every request)

bash
export async function renderInvoice(data: unknown): Promise<Uint8Array> {
  const res = await fetch("https://makespdf.com/api/v1/render", {
    method: "POST",
    headers: {
      Authorization: `Bearer ${process.env.MAKESPDF_API_KEY}`,
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      templateId: process.env.MAKESPDF_INVOICE_TEMPLATE_ID,
      data,
    }),
  });
  if (!res.ok) {
    throw new Error(`makesPDF ${res.status}: ${await res.text()}`);
  }
  return new Uint8Array(await res.arrayBuffer());
}

Only retry on 429 or 5xx (max 3 attempts, respect Retry-After); never blanket-retry 400/401/402/404. 402 means out of credits — top up at Settings → Billing. Full error table and retry helper in the integration skill.

Recipe 2: give your agent template powers via hosted MCP~60 seconds

No code. Register the hosted MCP server once and the agent gets schema-validated tools for validating, previewing, and saving DSL templates — plus the skill file as a fetchable resource. Authoring tools are free; only render_template (and render_markdown) are billed, at the same 1 credit per 10 pages as REST.

Step 1: create an API key

Create one at Settings → API Keys. The MCP endpoint uses the same Bearer auth as every other /api/v1/* endpoint.

Step 2: register the server (Claude Code)

bash
claude mcp add --transport http makespdf \
  https://makespdf.com/api/v1/mcp \
  --header "Authorization: Bearer mpdf_your_api_key"

Claude Desktop and Cursor use the same URL and header in their JSON config — see MCP server for the per-client files.

Step 3: verify, then prompt

bash
curl -X POST https://makespdf.com/api/v1/mcp \
  -H "Authorization: Bearer mpdf_your_api_key" \
  -H "Content-Type: application/json" \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
# -> JSON listing all eleven tools (validate_dsl, render_dsl_preview, ...)
text
Create an invoice template for Acme Corp with 3 line items, save it, and
show me the preview PDF.

The agent reads the skill resource, drafts DSL, calls validate_dsl, fixes issues, calls render_dsl_preview (free, watermarked), then save_template. Discovery methods answer without a key; tools/call needs the Bearer token.

Recipe 3: embed authoring tools in your app (Vercel AI SDK)~60 seconds

For custom apps that already call an LLM: wrap the same REST endpoints as tools your model can call, and fetch the skill live with a vendored fallback. REST-only coupling — no shared imports, no proxy LLM, no billing passthrough. BYOK only: the integrator supplies their own model key; MAKESPDF_API_KEY pays for renders alone. This shim is the inline precursor to @makespdf/sdk (MakespdfClient + tools() + getSkill(), OIDC-published) — same shape, pasteable today.

Step 1: live skill fetch with offline fallback

js
// skill.ts — fetch the canonical skill live; fall back to the vendored copy.
const SKILL_URL = "https://makespdf.com/skills/pdf-template-author.md";
let cached: string | null = null;

export async function getSkill(fallback?: string): Promise<string> {
  if (cached) return cached;
  try {
    const res = await fetch(SKILL_URL);
    if (!res.ok) throw new Error(`skill fetch ${res.status}`);
    cached = await res.text();
    return cached;
  } catch (err) {
    // Offline / deploy-frozen fallback: vendor once with
    //   curl -o pdf-template-author.md https://makespdf.com/skills/pdf-template-author.md
    // and pass its contents as `fallback`.
    if (fallback) {
      cached = fallback;
      return cached;
    }
    throw err;
  }
}

Step 2: REST-backed tools for your model

js
// makespdf-tools.ts — REST-only tools() shim (BYOK; no proxy, no billing passthrough).
import { tool } from "ai";
import { z } from "zod";

const dataSchema = z.record(z.string(), z.unknown()).optional();

export function makespdfTools(opts: { apiKey: string; baseUrl?: string }) {
  const base = (opts.baseUrl ?? "https://makespdf.com").replace(/\/$/, "");
  const headers = {
    Authorization: `Bearer ${opts.apiKey}`,
    "Content-Type": "application/json",
  };
  const post = async (path: string, body: unknown) => {
    const res = await fetch(`${base}${path}`, {
      method: "POST",
      headers,
      body: JSON.stringify(body),
    });
    if (!res.ok) throw new Error(`makesPDF ${res.status}: ${await res.text()}`);
    return res;
  };

  return {
    validate_dsl: tool({
      description: "Catalog + accessibility check for a DSL script. Cheap, no render.",
      inputSchema: z.object({ dsl: z.string(), data: dataSchema }),
      execute: async ({ dsl, data }) =>
        post("/api/v1/preview/validate", { dsl, data }).then((r) => r.json()),
    }),
    render_dsl_preview: tool({
      description: "Free watermarked preview PDF. Returns { pageCount, pdfBase64 }.",
      inputSchema: z.object({ dsl: z.string(), data: dataSchema }),
      execute: async ({ dsl, data }) => {
        const res = await post("/api/v1/preview", { dsl, data });
        const buf = Buffer.from(await res.arrayBuffer());
        return {
          pageCount: Number(res.headers.get("x-pages") ?? 0),
          pdfBase64: buf.toString("base64"),
        };
      },
    }),
    render_template: tool({
      description: "BILLED (1 credit / 10 pages). Render a saved templateId to a real PDF.",
      inputSchema: z.object({ templateId: z.string(), data: dataSchema }),
      execute: async ({ templateId, data }) => {
        const res = await post("/api/v1/render", { templateId, data });
        const buf = Buffer.from(await res.arrayBuffer());
        return {
          pageCount: Number(res.headers.get("x-pages") ?? 0),
          creditsDeducted: Number(res.headers.get("x-credits-deducted") ?? 0),
          pdfBase64: buf.toString("base64"),
        };
      },
    }),
  };
}

Step 3: wire it next to your model call

js
import { generateText } from "ai";
import { getSkill } from "./skill";
import { makespdfTools } from "./makespdf-tools";

const skill = await getSkill(); // or getSkill(vendoredFallbackText)
const { text } = await generateText({
  model: YOUR_MODEL, // your key, your account — makesPDF never proxies LLM calls
  system: skill + "\n\n" + "Use validate_dsl before every preview; only call render_template when the layout is confirmed.",
  tools: makespdfTools({ apiKey: process.env.MAKESPDF_API_KEY! }),
  prompt: "Create an invoice template for Acme Corp with 3 line items.",
});
Deliberately deferred: proxy-LLM / billing passthrough, Vue/Svelte adapters, and a prebuilt <MakespdfChat> widget. If you need one of these, tell us — demand decides what ships next.

What’s next

  • Application-integration skill — the full backend reference this page compresses: error table, retries, versioning, delivery patterns, per-language snippets.
  • MCP server — the ten hosted tools, per-client registration files, and verify steps.
  • Bring your own AI — skill-only flows and CLI-based authoring when MCP isn’t available.
  • API Reference — the REST endpoints every recipe above wraps.