#!/usr/bin/env python3
"""Render docs/zapier-billing.pdf — Zapier cost structure for Verae planning.
Platypus Paragraphs for bullets (never ListFlowable). Every table cell is a Paragraph.
Avoid '|' in body text (Helvetica renders it like a capital I).
"""
from __future__ import annotations
from pathlib import Path
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER, TA_LEFT
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.platypus import (
PageBreak,
Paragraph,
Preformatted,
SimpleDocTemplate,
Spacer,
Table,
TableStyle,
)
ROOT = Path(__file__).resolve().parents[1]
OUT = ROOT / "docs" / "zapier-billing.pdf"
NAVY = colors.HexColor("#0F2744")
TEAL = colors.HexColor("#1A6B6B")
SLATE = colors.HexColor("#334155")
RULE = colors.HexColor("#CBD5E1")
ROW = colors.HexColor("#F1F5F9")
HEAD_BG = colors.HexColor("#0F2744")
HEAD_FG = colors.white
CODE_BG = colors.HexColor("#F8FAFC")
def styles():
base = getSampleStyleSheet()
return {
"cover_kicker": ParagraphStyle(
"cover_kicker", parent=base["Normal"], fontName="Helvetica",
fontSize=10, textColor=TEAL, alignment=TA_CENTER, spaceAfter=10,
),
"cover_title": ParagraphStyle(
"cover_title", parent=base["Title"], fontName="Helvetica-Bold",
fontSize=24, leading=30, textColor=NAVY, alignment=TA_CENTER, spaceAfter=10,
),
"cover_sub": ParagraphStyle(
"cover_sub", parent=base["Normal"], fontName="Helvetica",
fontSize=11.5, leading=16, textColor=SLATE, alignment=TA_CENTER, spaceAfter=8,
),
"h1": ParagraphStyle(
"h1", parent=base["Heading1"], fontName="Helvetica-Bold",
fontSize=14.5, leading=18, textColor=NAVY, spaceBefore=14, spaceAfter=7,
),
"h2": ParagraphStyle(
"h2", parent=base["Heading2"], fontName="Helvetica-Bold",
fontSize=11.5, leading=15, textColor=TEAL, spaceBefore=10, spaceAfter=5,
),
"body": ParagraphStyle(
"body", parent=base["Normal"], fontName="Helvetica",
fontSize=9.4, leading=13, textColor=SLATE, alignment=TA_LEFT, spaceAfter=6,
),
"bullet": ParagraphStyle(
"bullet", parent=base["Normal"], fontName="Helvetica",
fontSize=9.4, leading=12.8, textColor=SLATE, leftIndent=14,
firstLineIndent=-10, spaceAfter=3,
),
"toc": ParagraphStyle(
"toc", parent=base["Normal"], fontName="Helvetica",
fontSize=10, leading=15, textColor=NAVY, leftIndent=6, spaceAfter=2,
),
"cell": ParagraphStyle(
"cell", parent=base["Normal"], fontName="Helvetica",
fontSize=7.8, leading=10.4, textColor=SLATE,
),
"cell_h": ParagraphStyle(
"cell_h", parent=base["Normal"], fontName="Helvetica-Bold",
fontSize=7.8, leading=10.4, textColor=HEAD_FG,
),
"code": ParagraphStyle(
"code", parent=base["Code"], fontName="Courier",
fontSize=7.6, leading=10.2, textColor=NAVY, backColor=CODE_BG,
leftIndent=4, rightIndent=4, spaceBefore=3, spaceAfter=7,
),
"caption": ParagraphStyle(
"caption", parent=base["Normal"], fontName="Helvetica-Oblique",
fontSize=8, leading=11, textColor=colors.HexColor("#64748B"), spaceAfter=8,
),
}
S = styles()
USABLE = letter[0] - 1.4 * inch
def P(text, style="body"):
return Paragraph(text, S[style])
def B(text):
return Paragraph(f"• {text}", S["bullet"])
def H1(text):
return Paragraph(text, S["h1"])
def H2(text):
return Paragraph(text, S["h2"])
def CODE(text):
return Preformatted(text.rstrip() + "\n", S["code"])
def tbl(headers, rows, widths=None):
if widths is None:
widths = [USABLE / len(headers)] * len(headers)
data = [[Paragraph(h, S["cell_h"]) for h in headers]]
for row in rows:
data.append([Paragraph(c, S["cell"]) for c in row])
t = Table(data, colWidths=widths, repeatRows=1)
cmds = [
("BACKGROUND", (0, 0), (-1, 0), HEAD_BG),
("VALIGN", (0, 0), (-1, -1), "TOP"),
("LEFTPADDING", (0, 0), (-1, -1), 4),
("RIGHTPADDING", (0, 0), (-1, -1), 4),
("TOPPADDING", (0, 0), (-1, -1), 3.5),
("BOTTOMPADDING", (0, 0), (-1, -1), 3.5),
("GRID", (0, 0), (-1, -1), 0.3, RULE),
("LINEBELOW", (0, 0), (-1, 0), 1, TEAL),
]
for i in range(1, len(data)):
if i % 2 == 0:
cmds.append(("BACKGROUND", (0, i), (-1, i), ROW))
t.setStyle(TableStyle(cmds))
return t
def header_footer(canvas, doc):
canvas.saveState()
w, h = letter
canvas.setFillColor(NAVY)
canvas.rect(0, h - 28, w, 28, fill=1, stroke=0)
canvas.setFillColor(colors.white)
canvas.setFont("Helvetica", 8)
canvas.drawString(0.7 * inch, h - 18, "Verae Time · Zapier billing brief")
canvas.drawRightString(w - 0.7 * inch, h - 18, "Cost structure and variable pricing")
canvas.setFillColor(TEAL)
canvas.rect(0, 0, w, 22, fill=1, stroke=0)
canvas.setFillColor(colors.white)
canvas.setFont("Helvetica", 8)
canvas.drawString(0.7 * inch, 8, "Internal planning · 18 August 2026 · USD list from zapier.com/pricing")
canvas.drawRightString(w - 0.7 * inch, 8, f"{doc.page}")
canvas.restoreState()
def cover_footer(canvas, doc):
canvas.saveState()
w, _ = letter
canvas.setFillColor(NAVY)
canvas.rect(0, 0, w, 56, fill=1, stroke=0)
canvas.setFillColor(TEAL)
canvas.rect(0, 56, w, 4, fill=1, stroke=0)
canvas.setFillColor(colors.white)
canvas.setFont("Helvetica", 9)
canvas.drawCentredString(w / 2, 28, "Confirm live rates before any customer quote")
canvas.setFont("Helvetica", 8)
canvas.drawCentredString(w / 2, 14, "18 August 2026")
canvas.restoreState()
def story():
out = []
out.append(Spacer(1, 1.5 * inch))
out.append(P("VERAE TIME · COMMERCIAL PLANNING", "cover_kicker"))
out.append(P("Zapier cost structure
and billing models", "cover_title"))
out.append(
P(
"How Zapier charges for Zaps, MCP, AI, Code, Agents, and embed — "
"with worked examples — so we can design Verae’s meter without "
"subsidizing or colliding with Zapier.",
"cover_sub",
)
)
out.append(Spacer(1, 14))
out.append(
tbl(
["Source", "As of", "Use"],
[
[
"zapier.com/pricing (official machine-readable page)",
"17–18 Aug 2026",
"Plan levels, task tiers, overflow, add-ons",
],
[
"Zapier “What is a task?” (updated June 2026)",
"June 2026",
"What counts; AI 1/3/5; MCP = 2",
],
[
"verae-zapier-middleware PLAN_LIMITS",
"This repo",
"Our second meter (timestamps / verify / batch)",
],
],
[2.5 * inch, 1.5 * inch, 2.5 * inch],
)
)
out.append(PageBreak())
out.append(H1("Contents"))
for line in [
"1. The one idea that unlocks the rest",
"2. What a task is (and is not)",
"3. Plan levels — what the customer can build",
"4. Variable pricing inside the task pool",
"5. Task tiers and list price",
"6. Overflow (pay-per-task)",
"7. Other Zapier products — different models",
"8. Worked examples",
"9. Verae’s meter today",
"10. How this should shape our billing",
"11. Planning checklist",
]:
out.append(P(line, "toc"))
out.append(
P(
"Companion narrative: docs/zapier-billing.md. Architecture guide: getting-started.md §9–10.",
"caption",
)
)
# 1
out.append(H1("1. The one idea that unlocks the rest"))
out.append(
P(
"Zapier does not charge per Zap, per connected app, or (on Professional) per seat. "
"It charges for successful work. You buy two things that travel together: "
"a plan level (feature set) and a task tier (monthly allowance). "
"Enterprise swaps the monthly reset for an annual task pool."
)
)
out.append(
P(
"Three other economies sit beside that pool, not inside it:"
)
)
out.append(
tbl(
["Economy", "Unit", "Used for"],
[
["Core Zapier", "Task", "Zaps, AI by Zapier, Code by Zapier, MCP, SDK"],
["Agents add-on", "Activity", "Zapier Agents — does not draw tasks"],
["Chatbots add-on", "Bot-count tier", "Zapier Chatbots — not task-metered"],
["Verae (us)", "Timestamp / verify / batch", "Middleware PLAN_LIMITS — a second invoice"],
],
[1.7 * inch, 1.8 * inch, 3.0 * inch],
)
)
out.append(
P(
"Verae never appears on the Zapier invoice unless we sign a White Label / "
"Powered by Zapier contract and resell Zapier usage ourselves.",
"caption",
)
)
# 2
out.append(H1("2. What a task is (and is not)"))
out.append(
P(
"A task is counted when Zapier successfully completes a unit of work. "
"Failed steps are free on Zapier (they can still cost us if we already accepted the job)."
)
)
out.append(
tbl(
["Counts as tasks", "Does not count"],
[
[
"Successful action in another product (Create Timestamp, Slack, Drive, Sheets, webhook)",
"The trigger — including polling every 1–15 minutes",
],
[
"Successful Zapier MCP tool execute (read or write)",
"Filter, Paths, Formatter, Delay, Looping, Sub-Zap, Digest, Manager, Storage",
],
[
"AI by Zapier and Code by Zapier (see multipliers / extra runtime)",
"Zapier Tables and Forms triggers and actions",
],
[
"SDK execute once beta pricing starts (today: beta is free)",
"Building or testing a Zap until a step succeeds in production",
],
],
[3.25 * inch, 3.25 * inch],
)
)
out.append(
P(
"One shared allowance for the whole account. There is no separate MCP budget. "
"A five-step Zap that is trigger + Filter + Formatter + Timestamp + Slack uses "
"two tasks, not five."
)
)
# 3
out.append(H1("3. Plan levels — what the customer can build"))
out.append(P("Plans are cumulative. Task volume is chosen separately (section 5)."))
out.append(
tbl(
["", "Free", "Professional", "Team", "Enterprise"],
[
["Seats", "1", "1", "25", "Unlimited"],
["Zap shape", "Two-step only", "Multi-step", "Multi-step", "Multi-step"],
["Polling", "15 min", "2 min", "1 min", "1 min"],
["Premium apps, webhooks", "No", "Yes", "Yes", "Yes"],
["Filters, Paths, Formatter, AI", "No", "Yes", "Yes", "Yes"],
["Shared Zaps, SAML SSO", "No", "No", "Yes", "Yes"],
["SCIM, app controls, TAM, BYOM", "No", "No", "No", "Yes"],
["Task cycle", "Monthly", "Monthly", "Monthly", "Annual pool"],
["Pay-per-task overflow", "No", "Optional", "Optional", "Custom"],
[
"Entry price (annual, USD/mo)",
"$0 (100 tasks)",
"$19.99 (750 tasks)",
"$69 (2,000 tasks)",
"Sales",
],
],
[1.55 * inch, 1.15 * inch, 1.35 * inch, 1.25 * inch, 1.2 * inch],
)
)
out.append(
P(
"14-day Professional trial, no card. Non-profit: extra 15% off the subscription, "
"not on pay-per-task. Live chat on Professional only at the 2,000+ task tier.",
"caption",
)
)
out.append(
P(
"Verae implication: a Free customer can only run “new file → Create Timestamp.” "
"A catalog write, Slack notify, or Wait plus another action requires Professional."
)
)
# 4
out.append(H1("4. Variable pricing inside the task pool"))
out.append(
P(
"Not every successful step costs one task. Zapier uses multipliers so expensive "
"compute consumes more of the same allowance. Confirm /pricing/rates before "
"a contract; the page was not fetchable at write time. These multipliers are what "
"Zapier publishes on the main pricing page and the June 2026 task article."
)
)
out.append(
tbl(
["Work", "Tasks per success", "Notes"],
[
["Typical third-party action (Verae Create Timestamp, Slack, Drive, Sheets)", "1", "The default. Design around this."],
["Standard AI by Zapier (default model)", "1", "Same as a normal action"],
["Advanced AI by Zapier", "3", "Confirm on the rate card"],
["Premium AI by Zapier", "5", "Confirm on the rate card"],
["Zapier MCP execute (read or write)", "2", "Discover / inspect / enable are free meta-tools"],
[
"Code by Zapier",
"0, then 1 per extra 30 s",
"Included: Free 1s, Pro/Team 30s, Enterprise 2 min. Extended runtime opt-in 1–8 min on paid.",
],
["Zapier SDK", "Free in beta", "Expect it to join the task pool when beta ends"],
],
[2.5 * inch, 1.5 * inch, 2.5 * inch],
)
)
out.append(
P(
"Failed steps = 0 Zapier tasks. Autoreplay and customer retries can still hit Verae. "
"That is our problem, not Zapier’s."
)
)
# 5
out.append(H1("5. Task tiers and list price (USD, August 2026)"))
out.append(
P(
"Self-serve is sold as plan × tier. Annual is about 33% off monthly. "
"Implied dollars per task = list price ÷ included tasks (order-of-magnitude only)."
)
)
out.append(H2("Professional"))
out.append(
tbl(
["Tasks / mo", "Annual / mo", "Monthly / mo", "Implied $/task (annual)"],
[
["750", "$19.99", "$29.99", "$0.027"],
["1,500", "$39.00", "$58.50", "$0.026"],
["2,000", "$49.00", "$73.50", "$0.025"],
["5,000", "$89.00", "$133.50", "$0.018"],
["10,000", "$129.00", "$193.50", "$0.013"],
["20,000", "$189.00", "$283.50", "$0.0095"],
["50,000", "$289.00", "$433.50", "$0.0058"],
["100,000", "$489.00", "$733.50", "$0.0049"],
["200,000", "$769.00", "$1,149.00", "$0.0038"],
["500,000", "$1,499.00", "$2,199.00", "$0.0030"],
["1,000,000", "$2,199.00", "$3,299.00", "$0.0022"],
["2,000,000", "$3,389.00", "$5,099.00", "$0.0017"],
],
[1.6 * inch, 1.6 * inch, 1.6 * inch, 1.7 * inch],
)
)
out.append(H2("Team (starts at 2,000)"))
out.append(
tbl(
["Tasks / mo", "Annual / mo", "Monthly / mo", "Implied $/task (annual)"],
[
["2,000", "$69.00", "$103.50", "$0.035"],
["5,000", "$119.00", "$178.50", "$0.024"],
["10,000", "$169.00", "$253.50", "$0.017"],
["20,000", "$249.00", "$373.50", "$0.012"],
["50,000", "$399.00", "$598.50", "$0.008"],
["100,000", "$599.00", "$898.50", "$0.006"],
["1,000,000", "$2,499.00", "$3,749.00", "$0.0025"],
["2,000,000", "$3,999.00", "$5,999.00", "$0.0020"],
],
[1.6 * inch, 1.6 * inch, 1.6 * inch, 1.7 * inch],
)
)
out.append(
P(
"Team’s entry dollars-per-task is higher than Pro because the customer is "
"buying seats, shared connections, and SSO — not cheaper tasks. Intermediate "
"tiers (300k, 400k, 750k, 1.25M, 1.5M, 1.75M) exist on the live page. Above 2M: Sales.",
"caption",
)
)
# 6
out.append(H1("6. Overflow (pay-per-task)"))
out.append(
tbl(
["Setting", "What happens"],
[
[
"On (paid plans)",
"Zaps and MCP keep running. Extra tasks bill at 1.25× the plan’s base dollars-per-task (annual) or 2.5× (monthly). Hard ceiling: 3× subscribed tasks, then pause.",
],
["Off", "Everything stops at the allowance."],
["Free", "No overflow. Hits 100 and stops."],
["Enterprise", "Annual pool instead of a monthly reset; overflow is contractual."],
],
[1.6 * inch, 4.9 * inch],
)
)
out.append(
P(
"Worked overage: Professional 750 annual → base ≈ $19.99 / 750 = $0.0267. "
"Overflow ≈ $0.0333 per task. Same plan billed monthly: base ≈ $0.0400, "
"overflow ≈ $0.100. Overflow on a monthly subscription is about 3× the "
"annual overflow rate."
)
)
# 7
out.append(H1("7. Other Zapier products — different models"))
out.append(
tbl(
["Product", "Model", "Relation to tasks"],
[
["Zap workflows", "Task", "Core"],
["Zapier MCP", "Same pool; 2 tasks per successful execute", "Meta-tools free"],
["Zapier SDK", "Beta free; expect tasks later", "Consume, not publish"],
["AI by Zapier", "Task × model tier (1 / 3 / 5)", "Inside the Zap"],
["Code by Zapier", "Included seconds + 1 task / 30 s extra", "Inside the Zap"],
["Tables / Forms", "Plan caps (records, pages, upload size)", "0 tasks when used in Zaps"],
[
"Agents",
"Activities (Free 400/mo; paid ~$33.33/mo annual for 1,500). Per-run cap 10 / 40.",
"Does not use tasks",
],
["Chatbots", "Bot-count tiers (Free 2; paid ≈5 / ≈20)", "Does not use tasks"],
["Canvas / Copilot", "Included; Free Copilot has a daily message limit", "Not a usage meter"],
[
"Directory integration (us)",
"$0 to publish. Customer’s Zapier plan pays tasks.",
"Partner is not billed",
],
[
"White Label / Powered by Zapier / embed",
"Usage-based to the product company. End users may not need their own Zapier bill.",
"Sales contract",
],
["NLA / AI Actions", "Retired", "Do not design around this"],
],
[1.8 * inch, 2.8 * inch, 1.9 * inch],
)
)
# 8
out.append(H1("8. Worked examples"))
out.append(
P(
"Assume Professional billed annually unless noted, and that every named "
"action succeeds. Verae units are our meter."
)
)
out.append(H2("A. Two-step timestamp (Free-capable)"))
out.append(P("New file in Drive → Verae: Create Timestamp · 1 Zapier task and 1 Verae stamp per file."))
out.append(
tbl(
["Volume", "Zapier tasks", "Fits", "Zapier $ (annual)", "Verae"],
[
["80 files / mo", "80", "Free", "$0", "80 stamps — over our free (50)"],
["200", "200", "Pro 750", "$19.99", "200 — still under our starter (500)"],
["600", "600", "Pro 750", "$19.99", "600 — over our starter; we 402 first"],
],
[1.3 * inch, 1.2 * inch, 1.1 * inch, 1.4 * inch, 1.5 * inch],
)
)
out.append(
P(
"Lesson: the customer can be fine on Zapier and still hit our 402. "
"The two meters trip at different points."
)
)
out.append(H2("B. Production pattern we recommend"))
out.append(
P(
"New file → Filter → Create Timestamp → Formatter → write catalog "
"(Zapier Tables = 0 tasks) → Slack. Billable: Timestamp + Slack = "
"2 tasks per file that passes the filter."
)
)
out.append(
tbl(
["Files / mo", "Pass filter", "Zapier tasks", "Cheapest Pro tier", "Zapier $", "Verae stamps"],
[
["1,000", "20% (200)", "400", "750", "$19.99", "200"],
["1,000", "100%", "2,000", "2,000", "$49", "1,000 (our pro)"],
["10,000", "100%", "20,000", "20,000", "$189", "10,000 (contract)"],
],
[1.05 * inch, 1.1 * inch, 1.05 * inch, 1.25 * inch, 0.95 * inch, 1.1 * inch],
)
)
out.append(
P(
"Filter early: 800 of 1,000 files dropped costs 0 Zapier tasks and 0 Verae stamps. "
"That is the single best cost control we can teach customers."
)
)
out.append(H2("C. Create and Wait versus hook"))
out.append(B("Wait in the Zap: 1 Zapier task. Fine when the user needs the certificate in the same run."))
out.append(
B(
"Async create + Timestamp Completed hook + update row: 1 task to create + 1 task "
"when the hook fires an action = 2 tasks, but the Zap does not sit open. Prefer at volume."
)
)
out.append(
B(
"Anti-pattern: polling Find Job Status every minute. Each successful search is a task."
)
)
out.append(H2("D. Same work via Zapier MCP (an agent)"))
out.append(
P(
"Agent executes Create Timestamp, write Sheet, Slack. Three executes × 2 tasks = "
"6 Zapier tasks per file, plus Agent activities if they used Zapier Agents."
)
)
out.append(
tbl(
["Files / mo", "Zapier tasks", "Cheapest Pro", "Zapier $", "Same stamps via Example B"],
[
["200", "1,200", "1,500 ($39)", "$39", "400 tasks / $19.99"],
["1,000", "6,000", "10,000 ($129)", "$129", "2,000 tasks / $49"],
],
[1.2 * inch, 1.2 * inch, 1.5 * inch, 1.1 * inch, 1.5 * inch],
)
)
out.append(
P(
"Planning rule: MCP-shaped clients burn Zapier 2× per hop. Do not price "
"Verae as if the customer’s only cost is our stamp."
)
)
out.append(H2("E. AI enrichment in the Zap"))
out.append(
P(
"New file → Standard AI extract (1) → Create Timestamp (1) → Sheet (1) = "
"3 tasks. Swap Premium AI: 5+1+1 = 7 tasks. "
"1,000 files: 3,000 vs 7,000 tasks → $89 vs $129 on Pro annual. "
"Model choice is the customer’s Zapier bill unless we bury AI inside our action "
"(we should not)."
)
)
out.append(H2("F. Overflow month"))
out.append(
P(
"Customer on Pro 750 annual ($19.99). A campaign pushes 1,400 successful "
"timestamp-only runs."
)
)
out.append(CODE(
"Included: 750\n"
"Overflow: 650 × ~$0.0333 ≈ $22\n"
"Total Zapier that month ≈ $42\n"
"Still under the 3× ceiling (2,250)\n"
"\n"
"Same usage on monthly Pro 750 ($29.99):\n"
"Overflow: 650 × ~$0.100 ≈ $65\n"
"Plus subscription ≈ $95"
))
out.append(H2("G. White Label — we are the billed party"))
out.append(
P(
"If Verae embeds Zapier and Zapier bills us per task, then 10,000 "
"customer files × 2 tasks = 20,000 tasks ≈ $189/mo at Pro annual list "
"plus our timestamp COGS. That number belongs in our COGS, not in the "
"end customer’s Zapier account."
)
)
# 9
out.append(H1("9. Verae’s meter today"))
out.append(P("From verae-zapier-middleware PLAN_LIMITS — independent of Zapier."))
out.append(
tbl(
["Verae plan", "Timestamps / mo", "Verifications", "Batch", "RPM"],
[
["free", "50", "50", "no", "30"],
["starter", "500", "500", "yes, max 10", "120"],
["pro", "5,000", "5,000", "yes, max 100", "600"],
["enterprise", "unlimited / contract", "contract", "yes", "3,000"],
],
[1.4 * inch, 1.5 * inch, 1.3 * inch, 1.2 * inch, 1.1 * inch],
)
)
out.append(
P(
"Over-quota → HTTP 402 QUOTA_EXCEEDED. Batch on free → 403 PLAN_UPGRADE_REQUIRED. "
"These fire whether or not Zapier is still inside its task allowance."
)
)
# 10
out.append(H1("10. How this should shape our billing"))
out.append(B(
"1. Never bundle “unlimited Zapier.” We do not control their task tier, "
"MCP multiplier, or overflow toggle."
))
out.append(B(
"2. Meter what we uniquely do: accepted timestamp jobs, verifies, maybe "
"stored GB / pin-days / Glacier restores — not Zap steps."
))
out.append(B(
"3. Mirror Zapier’s shape if we want familiarity: plan + included units + "
"optional overage with a ceiling. Customers already understand 402 versus pause."
))
out.append(B(
"4. Do not copy Zapier’s 2× MCP tax onto our API. POST /zapier/v1/timestamp "
"should stay one Verae unit whether the Zap used 1 task or an agent used 2."
))
out.append(B(
"5. Price batch as a plan gate. Zapier still charges 1 task for the batch "
"action. We decide whether 100 items cost 1 or 100 of our units. Today we count "
"batch size against batchMaxItems and timestamp quota."
))
out.append(B(
"6. Package for the hook pattern. Include enough monthly stamps that "
"create + hook + catalog is cheaper on our side than polling status."
))
out.append(B(
"7. Three motions, three packages. Directory user = they pay Zapier + they "
"pay Verae. MCP/agent user = they already pay 2 Zapier tasks per hop; keep us "
"simple. Embed/White Label = Zapier may bill us; either cover that COGS or sell "
"only the stamp and let them bring their own Zapier."
))
out.append(B(
"8. Storage / Peergos / Glacier is a third meter (getting-started §10). "
"Do not hide pin-days or restore fees inside “one timestamp.”"
))
out.append(B(
"9. Bill on accepted jobId (HTTP 202), not on Zapier’s success callback. "
"A failed later Zap step is free for them and already spent for us."
))
out.append(B(
"10. Quote two lines in every proposal: “Your Zapier plan (estimate N tasks)” "
"and “Verae (M timestamps).” Never a single blended number."
))
out.append(Spacer(1, 6))
out.append(
P(
"At Pro 750 annual, Zapier’s included work is about 2.7 cents per task. "
"If Create Timestamp is one Zapier task + one Verae stamp, the customer’s "
"Zapier share of a two-step Zap is ~$0.027. Price our stamp from chain/ops "
"cost, not 1:1 to that 2.7¢ — but if we charge dollars per stamp while Zapier "
"is cents per step, SMB Zaps will feel Verae-expensive even when Zapier is "
"the bigger invoice at volume."
)
)
# 11
out.append(H1("11. Planning checklist"))
out.append(B("For each target Zap, count billable Zapier actions, not steps on the canvas."))
out.append(B("Multiply MCP executes by 2."))
out.append(B("Apply AI 1 / 3 / 5 if they enrich in the Zap."))
out.append(B("Pick the cheapest Zapier tier that covers that task count (or run overflow math)."))
out.append(B("Count Verae timestamps / verifies / batch separately; check the 50 / 500 / 5,000 tripwires."))
out.append(B("If we embed Zapier, put Zapier list price into our COGS."))
out.append(B("Keep Peergos pin / Glacier restore off the timestamp SKU."))
out.append(B("Re-check zapier.com/pricing before any contract. This brief is dated 18 August 2026."))
out.append(Spacer(1, 14))
out.append(
P(
"Related: docs/zapier-billing.md · getting-started.md §9–10 · "
"docs/diagrams/11-zapier-billing.svg · LOGIN.md (customer must have a Zapier plan to run Zaps).",
"caption",
)
)
return out
def main():
OUT.parent.mkdir(parents=True, exist_ok=True)
doc = SimpleDocTemplate(
str(OUT),
pagesize=letter,
leftMargin=0.7 * inch,
rightMargin=0.7 * inch,
topMargin=0.55 * inch,
bottomMargin=0.45 * inch,
title="Zapier cost structure and billing models — Verae planning brief",
author="Verae / Zapier research workspace",
subject="Official Zapier pricing (Aug 2026) with examples for Verae billing design",
)
doc.build(story(), onFirstPage=cover_footer, onLaterPages=header_footer)
print(f"wrote {OUT} ({OUT.stat().st_size} bytes)")
if __name__ == "__main__":
main()