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-paywallThis creates two files:
my-paywall.json— A starter paywall schema with theme, products, and componentsmock-data.json— Mock product prices and user attributes for expression resolution
Validate the schema
npx paywall-dev validate my-paywall.jsonThe 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.jsonThis 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_screenshotEdit 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_idCLI Reference
paywall-dev init [name]
Create a starter paywall schema and mock data file.
| Option | Description | Default |
|---|---|---|
--template <type> | Template theme: basic, dark, fitness | basic |
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.
| Option | Description |
|---|---|
--watch, -w | Watch 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.
| Option | Description | Default |
|---|---|---|
--port, -p | Server port | 3456 |
--mock <path> | Path to custom mock data JSON | Auto-detected |
Server endpoints:
| Endpoint | Description |
|---|---|
GET /preview | HTML web preview in phone frame |
GET /schema | Raw paywall JSON (with ETag support) |
GET /mock-data | Mock product and user data |
GET /health | Server health check |
WebSocket / | Sends "reload" on file changes |
paywall-dev expressions <file>
List all {{ }} expressions in the schema and their resolved values.
| Option | Description |
|---|---|
--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 resolvedpaywall-dev export <file>
Push a finished paywall to the AgentWallie API.
| Option | Description |
|---|---|
--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:
| Field | Type | Required | Description |
|---|---|---|---|
slot | string | Yes | Slot identifier matching a slot in the paywall schema (e.g., "primary") |
label | string | Yes | Display label for the product (e.g., "Annual") |
price | string | Yes | Formatted display price (e.g., "$49.99") |
period | string | No | Billing period unit: "year", "month", "week", "lifetime" |
periodLabel | string | No | Short period suffix for display (e.g., "/yr", "/mo") |
pricePerMonth | string | No | Monthly equivalent price (e.g., "$4.17") |
trialPeriod | string | No | Trial duration text (e.g., "7 days", "1 month") |
trialPrice | string | No | Price during trial (e.g., "Free", "$0.99") |
savingsPercentage | number | No | Percentage savings vs monthly billing (e.g., 58) |
hasTrial | boolean | No | Whether 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 buildInstall and launch
xcrun simctl install booted path/to/PaywallDevPreview.app
xcrun simctl launch booted com.agentwallie.paywall-devScreenshot with mobile-mcp
mobile_take_screenshotThe 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:
| Path | Example | Description |
|---|---|---|
products.selected.price | $49.99 | Selected product's price |
products.selected.period_label | /yr | Selected product's period |
products.selected.trial_period | 7 days | Trial duration |
products.selected.savings_percentage | 58 | Savings vs monthly |
products.selected.has_trial | true | Whether trial exists |
products.primary.price | $49.99 | Product by slot name |
user.plan | free | User attribute |
user.session_count | 5 | User attribute |
theme.primary | #007AFF | Theme color |
theme.background | #FFFFFF | Theme 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:
paywall-dev init— Scaffold a template- Edit the JSON (add components, customize theme)
paywall-dev validate— Catch errors earlypaywall-dev serve— Start preview server- Use Playwright MCP to screenshot
/preview - Iterate: edit → save → auto-reload → screenshot
- For final verification: launch iOS preview app, use mobile-mcp screenshot
paywall-dev export— Push to production API