In development: free API keys by invite to waitlist members

Slipmint API docs

Slipmint turns JSON (or your own HTML) into a PDF. Everything on this page describes code that exists today. Anything else is marked planned.

Status

Quickstart

Base URL: https://slipmint-api.mike-tusa.workers.dev

curl

BASE=https://slipmint-api.mike-tusa.workers.dev
export SLIPMINT_API_KEY=YOUR_API_KEY   # free keys go out by invite to waitlist members

curl -X POST "$BASE/v1/pdf" \
  -H "Authorization: Bearer $SLIPMINT_API_KEY" \
  -H "Content-Type: application/json" \
  -o invoice.pdf \
  -d '{"template_id":"invoice","data":{"company_name":"Northwind Studio","brand_color":"#0f766e",
       "invoice_number":"INV-1042","issue_date":"2026-09-25","customer_name":"Acme Corp",
       "currency":"USD","tax_rate":8.25,
       "items":[{"description":"Website redesign","quantity":1,"unit_price":4500}]}}'

# Your own HTML instead of a template:
curl -X POST "$BASE/v1/pdf" -H "Authorization: Bearer $SLIPMINT_API_KEY" \
  -H "Content-Type: application/json" -o page.pdf \
  -d '{"html":"<h1>Hello</h1><style>@page{size:Letter}</style>","filename":"page.pdf"}'

# Check the rendered HTML without using a document from your quota:
curl -X POST "$BASE/v1/pdf?preview=html" -H "Authorization: Bearer $SLIPMINT_API_KEY" \
  -H "Content-Type: application/json" -d @invoice.json -o preview.html

JavaScript (Node 18+, ESM)

import { writeFile } from "node:fs/promises";

const BASE = "https://slipmint-api.mike-tusa.workers.dev";
const res = await fetch(`${BASE}/v1/pdf`, {
  method: "POST",
  headers: { Authorization: `Bearer ${process.env.SLIPMINT_API_KEY}`, "Content-Type": "application/json" },
  body: JSON.stringify({
    template_id: "certificate",
    data: {
      recipient_name: "Ada Lovelace", course_name: "Intro to Analytics",
      issued_date: "September 25, 2026", issuer_name: "Example Academy",
      brand_color: "#b45309", font: "EB Garamond", page_size: "Letter",
    },
  }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
console.log("usage:", res.headers.get("X-Slipmint-Usage"));
await writeFile("certificate.pdf", Buffer.from(await res.arrayBuffer()));

Python (requests)

import os, requests

BASE = "https://slipmint-api.mike-tusa.workers.dev"
resp = requests.post(
    f"{BASE}/v1/pdf",
    headers={"Authorization": f"Bearer {os.environ['SLIPMINT_API_KEY']}"},
    json={
        "template_id": "report",
        "data": {
            "company_name": "Brightline Agency", "report_title": "Monthly Report",
            "period": "September 2026", "brand_color": "#1d4ed8",
            "kpi_1_label": "Sessions", "kpi_1_value": "48,210",
            "summary": "First paragraph.\n\nSecond paragraph.",
            "rows": [{"label": "Organic", "value": "22,140"}, {"label": "Direct", "value": "12,030"}],
        },
    },
    timeout=60,
)
if resp.status_code != 200:
    raise SystemExit(f"{resp.status_code}: {resp.text}")
open("report.pdf", "wb").write(resp.content)

Authentication

Every /v1/* request needs Authorization: Bearer <api key>. Keys are stored only as SHA-256 hashes, so a lost key can't be recovered and has to be replaced. The pages /, /docs, /gallery/* and /health are public.

POST /v1/pdf

JSON body with exactly one of:

FieldTypeNotes
template_id + datastring + objectOne of invoice, receipt, report, certificate. data is validated against the template tokens below.
htmlstring, max 2 MBA full HTML document. Use CSS @page { size: A4 } or Letter to set the paper size.
filenamestring (optional)Used in Content-Disposition. Letters, digits, . _ -; max 80.
webhook_url(planned)Async render with a webhook. Returns 501 for now.

Response: 200 application/pdf with the header X-Slipmint-Usage: <used>/<included>. With ?preview=html you get 200 text/html instead, and it isn't counted.

Templates & tokens

All text values are HTML-escaped before they go into the template, so you can't inject markup through data. GET /v1/templates returns this same information as JSON, including sample data.

Invoice invoice (portrait)

Branded invoice with line items, computed subtotal/tax/total, status badge, payment details and notes. Preview

TokenTypeDefaultNotes
logo_urlurlhttps:// or data:image/ URL. Hidden when empty.
brand_colorcolor#2563ebHex color. Text on brand-colored areas switches to black/white automatically for contrast.
fontfontHelveticaFont family name installed on the renderer (letters, digits, spaces, hyphens; max 40).
page_sizepage_sizeA4A4 or Letter.
company_name requiredtext
company_addresslongtext
company_emailtext
invoice_number requiredtext
issue_date requiredtext
due_datetext
statustextBadge text, e.g. DUE, PAID, OVERDUE. Hidden when empty.
customer_name requiredtext
customer_addresslongtext
customer_emailtext
referencetextPO number / project reference.
itemsitems[{description, quantity, unit_price}]; subtotal/tax/total are computed.
currencycurrencyUSDISO 4217 code, e.g. USD, EUR, GBP.
localetexten-USBCP 47 locale used for number/currency formatting.
tax_ratenumberPercent, e.g. 8.25. Omit for no tax line.
tax_labeltextTaxLabel for the tax line.
payment_detailslongtextBank / payment link instructions.
noteslongtext
thank_you_notetext

Receipt receipt (portrait)

Payment receipt card with amount paid, payment method, line items and totals. Preview

TokenTypeDefaultNotes
logo_urlurlhttps:// or data:image/ URL. Hidden when empty.
brand_colorcolor#2563ebHex color. Text on brand-colored areas switches to black/white automatically for contrast.
fontfontHelveticaFont family name installed on the renderer (letters, digits, spaces, hyphens; max 40).
page_sizepage_sizeA4A4 or Letter.
company_name requiredtext
company_emailtext
receipt_number requiredtext
paid_at requiredtext
payment_methodtextCard
customer_nametext
itemsitems[{description, quantity, unit_price}]; subtotal/tax/total are computed.
currencycurrencyUSDISO 4217 code, e.g. USD, EUR, GBP.
localetexten-USBCP 47 locale used for number/currency formatting.
tax_ratenumberPercent, e.g. 8.25. Omit for no tax line.
tax_labeltextTaxLabel for the tax line.
thank_you_notetextThanks for your payment.

Simple report report (portrait)

One- or two-page report: branded cover band, three KPIs, summary paragraphs and a data table. Preview

TokenTypeDefaultNotes
logo_urlurlhttps:// or data:image/ URL. Hidden when empty.
brand_colorcolor#2563ebHex color. Text on brand-colored areas switches to black/white automatically for contrast.
fontfontHelveticaFont family name installed on the renderer (letters, digits, spaces, hyphens; max 40).
page_sizepage_sizeA4A4 or Letter.
company_name requiredtext
report_title requiredtext
periodtext
prepared_fortext
generated_attext
kpi_1_labeltext
kpi_1_valuetext
kpi_2_labeltext
kpi_2_valuetext
kpi_3_labeltext
kpi_3_valuetext
summarylongtextPlain text; blank lines start new paragraphs.
table_titletextDetails
rowsrows[{label, value, note?}] rendered as the data table.
footer_notetext

Certificate certificate (landscape)

Landscape certificate of completion with double brand border, seal and signature lines. Preview

TokenTypeDefaultNotes
logo_urlurlhttps:// or data:image/ URL. Hidden when empty.
brand_colorcolor#2563ebHex color. Text on brand-colored areas switches to black/white automatically for contrast.
fontfontEB GaramondFont family. Default: bundled EB Garamond (SIL OFL); other names must be installed on the renderer.
page_sizepage_sizeA4A4 or Letter.
certificate_titletextCertificate of Completion
recipient_name requiredtext
course_name requiredtext
descriptiontext
issued_date requiredtext
issuer_name requiredtext
signer_nametext
signer_titletext
seal_texttextShort text in the seal, e.g. the year. Max ~8 chars looks best.
certificate_idtext

Live previews rendered with sample data by the same code path as the PDF (open one full-size to print-preview it). Every name and number is made up.

Query options: ?page_size=A4|Letter and ?brand_color=7c3aed (hex without #).

Branding & page size

Other endpoints

RouteAuthReturns
GET /healthnoService status, whether a renderer is configured, and the store type.
GET /v1/templatesyesTemplates, tokens and sample data.
GET /v1/usageyesDocuments used this calendar month (UTC), plus included, overage and remaining.
GET /gallery/:idnoHTML preview with sample data.

Errors

StatuserrorMeaning
400invalid_json / invalid_bodyThe body isn't a JSON object.
401missing_api_key / invalid_api_keyCheck the Authorization header.
413payload_too_largehtml is over 2 MB.
422validation_faileddetails[] lists every problem found.
422blocked_urlThe HTML or logo_url references a private, loopback, link-local or cloud-metadata address (for example localhost, 10.x, 169.254.169.254). Only public URLs can be fetched.
404not_foundUnknown route, including unknown /v1/… paths.
429quota_exceededYou've hit your plan's hard monthly cap (Free plan).
501not_implementedNo renderer is configured, or you used a planned feature (webhook_url).
502renderer_unreachable / render_failedThe renderer failed. The document isn't counted.

Limits & quotas

Pricing (planned)

PlanPriceIncludedBeyond included
Free$050 documents / monthNone (hard cap, returns 429)
Indie$15 / month1,000 documents / monthThen $0.03 per extra document
Agency$49 / month5,000 documents / month, up to 25 brands (not enforced yet)Then $0.03 per extra document

No payments are taken yet. Brand limits aren't enforced in the API yet, because branding is sent per request.

Privacy & retention

Summary (the full policy is at /privacy):

API keys

Free API keys (50 documents per month) go out by invite to waitlist members. Join the waitlist on the home page and we'll send you one launch invite, in batches, with a personal, single-use link that creates your key. The link expires after 7 days, and the key is shown only once. See privacy §6 and §6a for what's stored and how to delete it.

Use of the API is subject to the beta terms.