> ## Documentation Index
> Fetch the complete documentation index at: https://docs.blinkpdf.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Stored AcroForm templates

> POST /v1/templates stores a blank AcroForm. Fill, inspect, and validate-fill accept templateId XOR pdf. Account-owned. No unit.

`POST /v1/templates` stores a blank AcroForm once and returns `{ id, title, fields, skipped }`. The stored object is the template, not a filled output.

Not `POST /v1/render`. Not `/v1/forms`. Not a Markdown [sandwich-document](/api-reference/overview) kit. Does not spend a unit.

Then send `{ "templateId": "<id>", "values": { … } }` to [inspect](/fill/inspect), [validate-fill](/fill/fill#validate-before-you-spend-a-unit), or [fill](/fill/fill) without resending the PDF.

## Store

JSON `{ "pdf": "<Base64>", "title": "Intake card" }` or multipart `pdf` plus optional `title`. Extra keys (`values`, `templateId`) are rejected. Same `x-api-key` as render.

`title` is the body field, or the upload filename if omitted. JSON without a filename must send `title`. An empty multipart `File.name` is omitted — same `400 missing_property` `#/title` as JSON without `title`.

```bash theme={null}
curl https://api.blinkpdf.io/v1/templates \
  --request POST \
  --header "x-api-key: ${BLINKPDF_API_KEY}" \
  --header 'Accept: application/json' \
  -F pdf=@intake.pdf \
  -F title='Intake card'
```

Rejects no `/AcroForm`, `/NeedAppearances`, a password, XFA, or a document signature — same codes as inspect and fill.

### Receive

| Key | Role |
| - | - |
| `id` | Account-owned stored id |
| `title` | Body title, or the filename when title was omitted |
| `fields`, `skipped` | Same inspect projection as [`POST /v1/inspect`](/fill/inspect) on these bytes |

No `warnings`. No `reason`. No fill `values`.

## Get, list, update, delete, download

| Method | Path | Result |
| - | - | - |
| `GET` | `/v1/templates` | Titled rows `{ id, title }`, ordered by title then id. Default `limit` 50; max 100. Pass `cursor` from `nextCursor` for the next page. Envelope: `{ "templates": [ … ], "nextCursor": "…" }`. |
| `GET` | `/v1/templates/{id}` | Title, `createdAt`, `byteSize`, cached inspect `fields` / `skipped`, and `downloadUrl`. Does not re-inspect. `Accept: application/pdf` streams the blank instead of JSON. |
| `GET` | `/v1/templates/{id}/download` | Streams the stored blank. `application/pdf`. Not a filled output. Not `list_pdfs`. |
| `PATCH` | `/v1/templates/{id}` | `title` and/or replacement `pdf` on the **same** id. Cached inspect refreshes when bytes change. Empty patch: `400`. |
| `DELETE` | `/v1/templates/{id}` | Gone, not hidden. Later get, download, or fill-by-id: `404`. |

`downloadUrl` is `GET /v1/templates/{id}/download` on this host, same `x-api-key`. Schema-only GET is not enough if you need the bytes.

Foreign or unknown id: `404`. Store outage: `503 service_unavailable` — never a silent drop.

## Ownership

Templates belong to the **account**, not the API key. Any `bp_` key (or OAuth principal) on that account can list, get, update, delete, download, and fill the same id. Rotating a key does not orphan the library. Another account's id is `404`.

## Caps

Object cap `getMaxTemplates`: **10 / 50 / 250** (Free / Pro / Business). Independent of kept MCP PDFs (10 / 100 / 1,000). Hitting one does not block the other. A full template cap is `409 template_limit` with `maxTemplates` and `current`.

Upload cap `maxTemplateBytes`: **5 / 10 / 20 MiB**. Oversize store or replace is `413 payload_too_large` and names `maxTemplateBytes`. Not render `bodyBytes`. Enterprise can raise this byte cap.

Fill-by-bytes still uses render `bodyBytes` (**128 KiB / 2 MiB / 5 MiB**). Fill-by-id does **not** re-check the stored PDF against `bodyBytes`. On **Free**, store the blank, then fill by id.

See [Pricing](/plans/pricing#stored-templates).

## Fill by id

```bash theme={null}
curl https://api.blinkpdf.io/v1/fill \
  --request POST \
  --header "x-api-key: ${BLINKPDF_API_KEY}" \
  --header 'Content-Type: application/json' \
  --header 'Accept: application/pdf' \
  --data '{"templateId":"<id>","values":{"givenName":"Ada","agree":true}}' \
  --output intake-filled.pdf
```

Inspect and validate-fill accept the same XOR. Sending both `pdf` and `templateId` is `400`.

<Note>
  Fill values are never stored on the template row. Account erasure deletes stored templates. See [Zero Retention](/concepts/zero-retention).
</Note>

MCP tools `store_template`, `list_templates`, `get_template`, `update_template`, and `delete_template` proxy this library. `list_pdfs` stays filled and generated outputs — it never mixes templates. See [MCP fill](/mcp/fill).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.