Sheet Smith
Template-to-PDF API. Turn JSON into clean, branded PDFs: quotes, invoices, receipts, certificates, packing slips and proposals, with real line items. Works from any HTTP client, Airtable buttons, Make and n8n.
Quickstart
- Get an API key. Keys are issued by the service operator during the beta. Your key looks like
ss_live_…; keep it secret. - Send a render request with a template id and your data. The response body is the PDF itself.
curl -X POST https://api.sheetsmith.pro/v1/render \
-H "Authorization: Bearer $SHEETSMITH_KEY" \
-H "Content-Type: application/json" \
-d '{
"template": "quote",
"data": {
"company": {"name": "Your Company LLC"},
"client": {"name": "Client Inc."},
"quote_number": "Q-1001",
"date": "2026-09-28",
"items": [
{"description": "Design work", "quantity": 10, "unit_price": 95},
{"description": "Hosting (1 year)", "quantity": 1, "unit_price": 240}
],
"tax_rate": 8.25
},
"branding": {"brand_color": "#1F5F8B"}
}' \
-o quote.pdf
Open quote.pdf. Totals are calculated for you. Browse every template and its example payload at Templates or GET https://api.sheetsmith.pro/v1/templates.
Render API
POST /v1/render. Authenticate with Authorization: Bearer <key> or X-API-Key: <key>. Returns application/pdf.
Body
| Field | Type | Description |
|---|---|---|
template | string, required | Template id: quote, invoice, receipt, certificate, packing_slip, proposal. |
data | object, required | Template fields (see each template's example). Unknown keys are ignored. |
branding | object, optional | Logo, brand color and font (below). |
options | object, optional | page_size (Letter default, or A4), filename, date_format (strftime, default %b %d, %Y). |
Line items and totals
Each item takes description, quantity (default 1), unit_price, and optionally details, sku, unit, line_total. Aliases qty, price/rate, amount and name also work. Totals use exact decimal math: subtotal = Σ quantity × unit_price, then discount or discount_percent, then tax or tax_rate (percent of subtotal minus discount), plus shipping. Any amount you send yourself is used as-is. Percentages are whole numbers: 8.25 means 8.25%. Set currency to an ISO code (default USD) and optionally currency_symbol. Up to 500 line items per document.
Branding
| Field | Description |
|---|---|
logo_url | Public https:// URL of a PNG, JPEG, SVG or WebP logo. |
logo_base64 | Or the image itself as base64 / data URI (max 1 MB). Use one or the other. |
brand_color | Hex color for headings and accents, e.g. #1F5F8B. |
font_family | Font name, e.g. Lato. Falls back to a clean sans-serif if unavailable. |
font_url | Public URL of a TTF/OTF/WOFF/WOFF2 file to load that font. |
Remote files must be on public http(s) hosts (ports 80/443), are fetched with a short timeout and size limit, and private or internal addresses are refused.
Response headers
| Header | Meaning |
|---|---|
X-Usage-Count | Documents rendered by this key in the current UTC month (including this one). |
X-Usage-Limit | Monthly limit for the key's plan (unlimited if none). |
X-Page-Count | Pages in the PDF. |
X-Watermark | true on the free plan. |
Content-Disposition | inline; filename="quote-Q-1001.pdf" (from the document number). |
X-Render-Warnings | Present when a remote logo or font could not be loaded (the PDF still renders). |
Errors
Errors are JSON: {"error": {"code": "...", "message": "...", "details": [{"field": "data.items[2].unit_price", "message": "..."}]}}
| Status | Code | When |
|---|---|---|
| 401 | missing_api_key, invalid_api_key | No key, unknown key or revoked key. |
| 413 | payload_too_large | Request body over 2 MB. |
| 422 | validation_error, unknown_template | Missing required fields, bad values, blocked URLs, too many line items. details lists each field. |
| 429 | monthly_limit_reached | The key's monthly document limit is used up. Includes limit, used and period. |
Templates
All templates accept the same branding. Required fields are marked; everything else is optional.
Quote / Estimate quote
Quote with company and client blocks, line items, discount, tax, totals, notes, terms and an acceptance block.
Required: company, client, quote_number, date, items
{
"template": "quote",
"data": {
"company": {
"name": "Harborline Digital Studio LLC",
"address": "410 Example Avenue, Suite 12\nPortland, OR 97201\nUnited States",
"email": "hello@harborline.example.com",
"phone": "+1 (503) 555-0142",
"website": "harborline.example.com",
"tax_id": "EIN 00-0000000"
},
"client": {
"name": "Cedar & Pine Outfitters Inc.",
"contact_name": "Jordan Alvarez",
"address": "88 Sample Road\nBoise, ID 83702",
"email": "jordan@cedarpine.example.com",
"phone": "+1 (208) 555-0199"
},
"quote_number": "Q-2026-0142",
"date": "2026-09-28",
"valid_until": "2026-10-28",
"project": "E-commerce storefront refresh",
"prepared_by": "Rowan Ellis",
"currency": "USD",
"items": [
{
"description": "Discovery workshop",
"details": "Half-day session with stakeholders, requirements summary",
"quantity": 1,
"unit_price": 1200
},
{
"description": "UX wireframes",
"details": "Home, category, product, cart, checkout",
"quantity": 5,
"unit": "pages",
"unit_price": 350
},
{
"description": "Visual design system",
"quantity": 1,
"unit_price": 2400
},
{
"description": "Front-end development",
"quantity": 64,
"unit": "hrs",
"unit_price": 115
},
{
"description": "Product data migration",
"details": "Up to 1,500 SKUs from CSV",
"quantity": 1,
"unit_price": 950
},
{
"description": "Launch support",
"quantity": 8,
"unit": "hrs",
"unit_price": 95
}
],
"discount_percent": 5,
"discount_label": "Returning client discount",
"tax_rate": 0,
"notes": "Pricing assumes content (copy and product photos) is supplied by Cedar & Pine before development starts.",
"terms": "50% deposit due on acceptance, balance due on launch. Quote valid for 30 days. Additional work billed at $115/hr."
},
"branding": {
"brand_color": "#1F5F8B"
}
}
Invoice invoice
Invoice with bill-to/ship-to, due date, PO number, line items, tax, amount paid, balance due and payment instructions.
Required: company, bill_to, invoice_number, date, items
{
"template": "invoice",
"data": {
"company": {
"name": "Harborline Digital Studio LLC",
"address": "410 Example Avenue, Suite 12\nPortland, OR 97201\nUnited States",
"email": "hello@harborline.example.com",
"phone": "+1 (503) 555-0142",
"website": "harborline.example.com",
"tax_id": "EIN 00-0000000"
},
"bill_to": {
"name": "Cedar & Pine Outfitters Inc.",
"contact_name": "Jordan Alvarez",
"address": "88 Sample Road\nBoise, ID 83702",
"email": "jordan@cedarpine.example.com",
"phone": "+1 (208) 555-0199"
},
"ship_to": null,
"invoice_number": "INV-2026-0317",
"date": "2026-09-28",
"due_date": "2026-10-28",
"po_number": "PO-55120",
"payment_terms": "Net 30",
"currency": "USD",
"items": [
{
"description": "Front-end development (September)",
"quantity": 42,
"unit": "hrs",
"unit_price": 115
},
{
"description": "Hosting & maintenance plan",
"details": "Monthly retainer",
"quantity": 1,
"unit_price": 450
},
{
"description": "Stock photography license",
"quantity": 12,
"unit_price": 18.5
},
{
"description": "Rush turnaround surcharge",
"quantity": 1,
"unit_price": 300
}
],
"tax_rate": 8.25,
"tax_label": "Sales tax",
"amount_paid": 2000,
"payment_instructions": "Bank transfer: Example Bank, Routing 000000000, Account 0000123456 (fictitious)\nPlease include the invoice number as the payment reference.",
"notes": "Thank you for your business!"
},
"branding": {
"brand_color": "#2E6B4F"
}
}
Receipt receipt
Payment receipt with items, tax, payment method and transaction ID.
Required: company, receipt_number, date, items
{
"template": "receipt",
"data": {
"company": {
"name": "Little Fern Café",
"address": "12 Placeholder Lane\nAsheville, NC 28801",
"phone": "+1 (828) 555-0107",
"website": "littlefern.example.com"
},
"customer": {
"name": "Sam Rivera",
"email": "sam.rivera@example.com"
},
"receipt_number": "R-000981",
"date": "2026-09-28",
"payment_method": "Visa •••• 4242",
"transaction_id": "txn_test_7F3K9Q",
"currency": "USD",
"items": [
{
"description": "Catering platter - Mediterranean",
"quantity": 2,
"unit_price": 64
},
{
"description": "Cold brew growler (64 oz)",
"quantity": 3,
"unit_price": 18
},
{
"description": "Seasonal pastry box",
"quantity": 1,
"unit_price": 32
},
{
"description": "Delivery",
"quantity": 1,
"unit_price": 15
}
],
"tax_rate": 7,
"notes": "Thanks for supporting local! Questions? Reply to your email receipt."
},
"branding": {
"brand_color": "#8A5A2B"
}
}
Certificate certificate
Landscape certificate of completion/achievement with signature lines.
Required: recipient_name, achievement, date
{
"template": "certificate",
"data": {
"title": "Certificate of Completion",
"recipient_name": "Avery Thompson",
"achievement": "Airtable Automations Masterclass",
"description": "Successfully completed 12 hours of instruction and a final project on workflow automation.",
"date": "2026-09-28",
"certificate_id": "CERT-2026-04417",
"company": {
"name": "Northwind Learning Collective",
"website": "northwind.example.com"
},
"signatories": [
{
"name": "Dana Whitfield",
"title": "Lead Instructor"
},
{
"name": "Morgan Lee",
"title": "Program Director"
}
]
},
"branding": {
"brand_color": "#6B4C9A"
}
}
Packing Slip packing_slip
Packing slip with ship-to, shipment details and SKU / ordered / shipped quantities (no prices).
Required: company, ship_to, order_number, date, items
{
"template": "packing_slip",
"data": {
"company": {
"name": "Bramble & Co. Home Goods",
"address": "900 Warehouse Way, Unit B\nReno, NV 89502",
"email": "orders@bramble.example.com",
"phone": "+1 (775) 555-0163"
},
"ship_to": {
"name": "Priya Natarajan",
"address": "27 Specimen Street, Apt 4C\nMadison, WI 53703",
"phone": "+1 (608) 555-0118"
},
"order_number": "BR-104522",
"date": "2026-09-26",
"ship_date": "2026-09-28",
"shipping_method": "UPS Ground",
"tracking_number": "1Z999AA10123456784",
"package_count": 2,
"weight": "14.2 lb",
"items": [
{
"sku": "LIN-QN-OAT",
"description": "Stonewashed linen duvet cover - Queen, Oat",
"quantity_ordered": 1,
"quantity": 1
},
{
"sku": "LIN-PC-OAT",
"description": "Linen pillowcase pair - Oat",
"quantity_ordered": 2,
"quantity": 2
},
{
"sku": "CER-MUG-SG",
"description": "Stoneware mug - Sage",
"quantity_ordered": 4,
"quantity": 4
},
{
"sku": "CND-CED-8",
"description": "Cedar & smoke candle, 8 oz",
"quantity_ordered": 2,
"quantity": 1,
"details": "1 unit backordered - ships separately"
}
],
"notes": "Backordered items ship at no extra cost. Returns accepted within 30 days."
},
"branding": {
"brand_color": "#37474F"
}
}
Proposal proposal
Multi-section proposal with summary, sections, milestones, optional pricing table and acceptance block.
Required: title, company, client, date
{
"template": "proposal",
"data": {
"title": "Operations Dashboard & Automation Proposal",
"company": {
"name": "Harborline Digital Studio LLC",
"address": "410 Example Avenue, Suite 12\nPortland, OR 97201\nUnited States",
"email": "hello@harborline.example.com",
"phone": "+1 (503) 555-0142",
"website": "harborline.example.com",
"tax_id": "EIN 00-0000000"
},
"client": {
"name": "Cedar & Pine Outfitters Inc.",
"contact_name": "Jordan Alvarez",
"address": "88 Sample Road\nBoise, ID 83702",
"email": "jordan@cedarpine.example.com",
"phone": "+1 (208) 555-0199"
},
"proposal_number": "P-2026-021",
"date": "2026-09-28",
"valid_until": "2026-10-31",
"prepared_by": "Rowan Ellis, Principal Consultant",
"summary": "Cedar & Pine currently tracks orders, inventory and supplier quotes across four spreadsheets. This proposal outlines a single Airtable base with automated quote and invoice generation, cutting manual admin by an estimated 6-8 hours per week.",
"sections": [
{
"heading": "Goals",
"body": "1. One source of truth for orders, inventory and suppliers.\n2. Quotes and invoices generated from records in one click.\n3. Weekly inventory report emailed automatically."
},
{
"heading": "Approach",
"body": "We start with a two-hour discovery call to map current workflows, then build the base iteratively with a review at each milestone. Your team gets a recorded walkthrough and written runbook at hand-off."
},
{
"heading": "What's not included",
"body": "Third-party subscription costs (Airtable, automation platforms) and data clean-up beyond the first 2,000 records."
}
],
"milestones": [
{
"name": "Discovery & data model",
"timing": "Week 1",
"description": "Workflow mapping, base schema sign-off"
},
{
"name": "Base build & migration",
"timing": "Weeks 2-3",
"description": "Tables, views, import of existing spreadsheets"
},
{
"name": "Automations & documents",
"timing": "Week 4",
"description": "Quote/invoice PDFs, inventory report"
},
{
"name": "Training & hand-off",
"timing": "Week 5",
"description": "Live session, recording, runbook"
}
],
"currency": "USD",
"items": [
{
"description": "Discovery & data model",
"quantity": 1,
"unit_price": 1500
},
{
"description": "Base build & migration",
"quantity": 1,
"unit_price": 4200
},
{
"description": "Automations & document templates",
"quantity": 1,
"unit_price": 2800
},
{
"description": "Training & hand-off",
"quantity": 1,
"unit_price": 900
}
],
"terms": "40% on signature, 40% at milestone 3, 20% on hand-off. Proposal valid until the date above."
},
"branding": {
"brand_color": "#1F5F8B"
}
}
Airtable button setup
Add a button to any Airtable record that opens its PDF, pulling the record and its linked line-item records. No scripting or automations needed.
1. Create a personal access token
- In Airtable, open Builder hub → Personal access tokens → Create token.
- Add the scope
data.records:readonly. - Under Access, add just the one base that holds your quotes.
- Copy the token (it starts with
pat).
2. Create a connection
Tell the service which table to read and how your fields map to the template. field_map maps template fields to your Airtable field names; use dotted names for the client block (client.name). Put constant values such as your own company details in static_data.
curl -X POST https://api.sheetsmith.pro/v1/airtable/connections \
-H "Authorization: Bearer $SHEETSMITH_KEY" \
-H "Content-Type: application/json" \
-d '{
"name": "Quotes",
"pat": "patXXXXXXXXXXXXXX.XXXXXXXX",
"base_id": "appXXXXXXXXXXXXXX",
"table": "Quotes",
"template": "quote",
"field_map": {
"quote_number": "Quote #",
"date": "Date",
"valid_until": "Valid Until",
"client.name": "Client Name",
"client.email": "Client Email",
"client.address": "Client Address",
"tax_rate": "Tax Rate",
"discount_percent": "Discount",
"notes": "Notes"
},
"static_data": {
"company": {"name": "Your Company LLC", "email": "hello@example.com"},
"currency": "USD",
"terms": "50% deposit on acceptance."
},
"line_items": {
"link_field": "Line Items",
"table": "Line Items",
"field_map": {"description": "Item", "quantity": "Qty", "unit_price": "Unit Price",
"line_total": "Amount", "details": "Notes"}
},
"logo_field": "Logo",
"branding": {"brand_color": "#1F5F8B"}
}'
The token is checked with one Airtable call. If it is invalid, lacks data.records:read or can't see the base/table, you get a clear error. The response includes your button formula:
CONCATENATE("https://api.sheetsmith.pro/v1/airtable/b/<link-token>/", RECORD_ID())
POST /v1/airtable/connections/{id}/rotate-link issues a new one (the old link stops working).3. Add the Button field
- In the Quotes table add a field of type Button, label it e.g. PDF.
- Action: Open URL. Paste the formula from step 2 as the URL formula.
- Click the button on any record: the PDF opens in a new tab.
Field types
- Currency, number and rollup fields become exact decimals. Lookup fields are flattened (first value for numbers, comma-joined for text).
- Percent fields arrive from Airtable as fractions (0.08) and are converted to 8%. Set
"percent_format": "whole"if your field stores 8. - Date fields are formatted like Sep 28, 2026 (change with
options.date_format). - Date-time fields arrive from Airtable in UTC. Set
"timezone"on the connection to an IANA name such as"America/New_York"so a quote created at 11 pm local time gets the right date. Default"UTC". Plain date fields are never shifted. logo_fieldcan be an attachment field (first image is used) or a URL field.- Line items keep the order shown in the linked-record field.
Each button link is rate-limited per plan: free keys get 5 PDFs per minute and 30 per hour; paid keys get 30 per minute and 300 per hour. Beyond that a friendly "try again shortly" page is shown. Clicks over the limit never call Airtable and never count against your monthly quota.
Manage connections with GET /v1/airtable/connections, PATCH /v1/airtable/connections/{id} (e.g. {"enabled": false} or a new pat) and DELETE /v1/airtable/connections/{id}. The token is never returned by the API.
Make
Use the built-in HTTP → Make a request module. (A dedicated Make app is planned; it is not available yet.)
| Setting | Value |
|---|---|
| URL | https://api.sheetsmith.pro/v1/render |
| Method | POST |
| Headers | Authorization: Bearer ss_live_… |
| Body type | Raw, content type JSON (application/json) |
| Request content | Your JSON, e.g. {"template": "invoice", "data": {…}}, mapping values from earlier modules. |
| Parse response | No. The response is a binary file: the module's Data output is the PDF. |
Pass the Data output to Google Drive "Upload a file", Gmail/Email attachments or Airtable. Name the file e.g. quote-{{number}}.pdf.
n8n
Use the HTTP Request node:
| Setting | Value |
|---|---|
| Method | POST |
| URL | https://api.sheetsmith.pro/v1/render |
| Authentication | Generic credential → Header Auth: name Authorization, value Bearer ss_live_… |
| Send Body | On. Body content type JSON, "Using JSON" with your payload (expressions allowed). |
| Options → Response | Response format File; put output in field data. |
The binary data property can go straight into Send Email, Google Drive or Write Binary File nodes.
Free plan & limits
- Free plan: 50 documents per calendar month (UTC), counted per API key. Free PDFs carry a small footer: Made with Sheet Smith - free plan.
- Paid plan: no footer, 2000 documents per month.
- Airtable button clicks count against the connection owner's key the same way.
- When the limit is reached the API returns
429 monthly_limit_reached(the Airtable button shows a short message page instead). - Requests up to 2 MB, up to 500 line items per document.
Data retention
- PDFs are generated in memory and returned directly. They are not stored. Request bodies are not stored either.
- We keep your API key only as a one-way hash, plus a monthly document counter per key.
- Airtable personal access tokens are stored encrypted at rest and are never returned by the API, shown in URLs or written to logs. Button links contain a random link token, not your Airtable token.
- Airtable data is fetched only when a button is clicked, used to build that one PDF, and discarded.
- Delete a connection (
DELETE /v1/airtable/connections/{id}) to remove its token and disable its button link immediately. Revoking the token in Airtable also cuts access.