# Integrate makesPDF into your app

> For AI agents: a documentation index is available at https://makespdf.com/llms.txt — every docs page also has a markdown twin (append .md to the page URL), including this one: https://makespdf.com/docs/integrate.md

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… | Use | Lives |
| --- | --- | --- |
| Production PDFs from your backend with data you already have | Recipe 1: backend render | This page |
| Your existing agent (Claude Code, Cursor, Desktop) authoring templates with schema-validated tools, zero code | Recipe 2: hosted MCP | This page + [MCP server](https://makespdf.com/docs/mcp) |
| Authoring tools embedded in your own app next to your own model calls | Recipe 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](https://makespdf.com/skills/pdf-integration.md).

## 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: 10,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)

```ts
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](https://makespdf.com/settings/billing). Full error table and retry
helper in the [integration skill](https://makespdf.com/skills/pdf-integration.md).

## 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](https://makespdf.com/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"
```

Cursor takes the same URL and header in its JSON config. Claude Desktop's config
file only launches stdio servers, so it needs the `mcp-remote` bridge — see
[MCP server](https://makespdf.com/docs/mcp) for both snippets.

### 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

```ts
// 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

```ts
// 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

```ts
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](https://makespdf.com/skills/pdf-integration.md) — the full
  backend reference this page compresses: error table, retries, versioning,
  delivery patterns, per-language snippets.
- [MCP server](https://makespdf.com/docs/mcp) — the ten hosted tools, per-client
  registration files, and verify steps.
- [Bring your own AI](https://makespdf.com/docs/ai-setup) — skill-only flows and CLI-based
  authoring when MCP isn’t available.
- [API Reference](https://makespdf.com/docs/api) — the REST endpoints every recipe above
  wraps.
