Paywall Dev Kit

Paywall Dev Kit

@agentwallie/paywall-dev is a local development toolkit for building, validating, and previewing paywall templates without needing the full API stack. It is designed for coding agents to iterate on paywall designs with a fast feedback loop.

This tool runs entirely locally. No API key, network connection, or project setup required for development. Export to the API when your template is ready.

Quick Start

Initialize a paywall

npx paywall-dev init my-paywall

This creates two files:

  • my-paywall.json — A starter paywall schema with theme, products, and components
  • mock-data.json — Mock product prices and user attributes for expression resolution

Validate the schema

npx paywall-dev validate my-paywall.json

The validator checks:

  • Schema correctness — Zod validation against the full paywall schema spec
  • Duplicate component IDs — Recursively checks through container children
  • Expression validity — Verifies {{ }} expressions use valid namespaces (products, user, theme)
  • Theme completeness — Ensures referenced theme keys exist
  • Product slot references — CTA buttons reference defined product slots
  • Restore action — Warns if no restore link exists (App Store requirement)
  • Color formats — Validates hex colors and theme references

Start the dev server

npx paywall-dev serve my-paywall.json

This starts a local server at http://localhost:3456 with:

  • /preview — Web preview in a phone-shaped frame (390×844px)
  • /schema — Raw JSON schema (for the iOS preview app)
  • /mock-data — Mock product and user data
  • WebSocket live reload — Preview updates automatically when you save the file

Preview and iterate

Open http://localhost:3456/preview in a browser, or use Playwright MCP:

browser_navigate http://localhost:3456/preview
browser_take_screenshot

Edit the JSON, save, and the preview reloads instantly.

Export when ready

npx paywall-dev export my-paywall.json \
  --api-key sk_your_key \
  --project your_project_id

CLI Reference

paywall-dev init [name]

Create a starter paywall schema and mock data file.

OptionDescriptionDefault
--template <type>Template theme: basic, dark, fitnessbasic

Templates set the color theme:

  • basic — White background, blue primary (#007AFF)
  • dark — Dark background (#1C1C1E), blue primary (#0A84FF)
  • fitness — Black background, orange primary (#FF6B35)

paywall-dev validate <file>

Validate a paywall schema and report errors/warnings.

OptionDescription
--watch, -wWatch for changes and re-validate continuously

Exit code: 0 if no errors, 1 if errors found.

paywall-dev serve <file>

Start the development server with web preview and live reload.

OptionDescriptionDefault
--port, -pServer port3456
--mock <path>Path to custom mock data JSONAuto-detected

Server endpoints:

EndpointDescription
GET /previewHTML web preview in phone frame
GET /schemaRaw paywall JSON (with ETag support)
GET /mock-dataMock product and user data
GET /healthServer health check
WebSocket /Sends "reload" on file changes

paywall-dev expressions <file>

List all {{ }} expressions in the schema and their resolved values.

OptionDescription
--mock <path>Path to custom mock data JSON

Example output:

Expression                      Value     Source
--------------------------------------------------
theme.background                #FFFFFF   resolved
products.selected.price         $49.99    resolved
products.selected.period_label  /yr       resolved
theme.primary                   #007AFF   resolved

paywall-dev export <file>

Push a finished paywall to the AgentWallie API.

OptionDescription
--api-key <key>AgentWallie private API key (required)
--project <id>Project ID (required)
--name <name>Override paywall name
--slug <slug>Set paywall slug

Mock Data

The mock-data.json file defines the data used to resolve expressions during preview:

{
  "products": [
    {
      "slot": "primary",
      "label": "Annual",
      "price": "$49.99",
      "period": "year",
      "periodLabel": "/yr",
      "pricePerMonth": "$4.17",
      "trialPeriod": "7 days",
      "trialPrice": "Free",
      "savingsPercentage": 58,
      "hasTrial": true
    },
    {
      "slot": "secondary",
      "label": "Monthly",
      "price": "$9.99",
      "period": "month",
      "periodLabel": "/mo"
    }
  ],
  "selectedProductIndex": 0,
  "userAttributes": {
    "plan": "free",
    "session_count": 5
  }
}

Mock Data Field Reference

products array

Each entry in the products array represents a product slot with the following fields:

FieldTypeRequiredDescription
slotstringYesSlot identifier matching a slot in the paywall schema (e.g., "primary")
labelstringYesDisplay label for the product (e.g., "Annual")
pricestringYesFormatted display price (e.g., "$49.99")
periodstringNoBilling period unit: "year", "month", "week", "lifetime"
periodLabelstringNoShort period suffix for display (e.g., "/yr", "/mo")
pricePerMonthstringNoMonthly equivalent price (e.g., "$4.17")
trialPeriodstringNoTrial duration text (e.g., "7 days", "1 month")
trialPricestringNoPrice during trial (e.g., "Free", "$0.99")
savingsPercentagenumberNoPercentage savings vs monthly billing (e.g., 58)
hasTrialbooleanNoWhether this product has a free trial

selectedProductIndex

Zero-based index into the products array indicating the initially selected product. Defaults to 0.

userAttributes

An object with arbitrary key-value pairs representing the current user. These values are accessible in expressions via the user.* namespace.

{
  "userAttributes": {
    "plan": "free",
    "session_count": 5,
    "onboarding_complete": true,
    "signup_date": "2025-01-15"
  }
}
⚠️

Mock data keys use camelCase (e.g., pricePerMonth, hasTrial), but expression paths in the schema use snake_case (e.g., products.selected.price_per_month, products.selected.has_trial). The CLI automatically maps between these formats during expression resolution.

Customize mock data to match your app's products and user segments.

Native iOS Preview

For pixel-perfect native rendering, use the PaywallDevPreview iOS app with the simulator:

Build the preview app

cd packages/paywall-dev-ios
xcodegen generate
xcodebuild -scheme PaywallDevPreview -sdk iphonesimulator build

Install and launch

xcrun simctl install booted path/to/PaywallDevPreview.app
xcrun simctl launch booted com.agentwallie.paywall-dev

Screenshot with mobile-mcp

mobile_take_screenshot

The iOS app connects to http://localhost:3456/schema, renders the paywall using the real AgentWallieKit SDK renderer, and polls for changes every second (with ETag caching).

Expression System

Expressions use {{ path }} syntax and are resolved against mock data:

PathExampleDescription
products.selected.price$49.99Selected product's price
products.selected.period_label/yrSelected product's period
products.selected.trial_period7 daysTrial duration
products.selected.savings_percentage58Savings vs monthly
products.selected.has_trialtrueWhether trial exists
products.primary.price$49.99Product by slot name
user.planfreeUser attribute
user.session_count5User attribute
theme.primary#007AFFTheme color
theme.background#FFFFFFTheme color

Condition System

Components can be conditionally shown/hidden:

{
  "type": "text",
  "id": "trial_info",
  "condition": {
    "field": "products.selected.has_trial",
    "operator": "is",
    "value": true
  },
  "props": { "content": "Includes free trial" }
}

Supported operators: is, is_not, gt, gte, lt, lte, contains, exists, not_exists.

Agent Workflow

The typical workflow for an AI coding agent:

  1. paywall-dev init — Scaffold a template
  2. Edit the JSON (add components, customize theme)
  3. paywall-dev validate — Catch errors early
  4. paywall-dev serve — Start preview server
  5. Use Playwright MCP to screenshot /preview
  6. Iterate: edit → save → auto-reload → screenshot
  7. For final verification: launch iOS preview app, use mobile-mcp screenshot
  8. paywall-dev export — Push to production API