Paywall Schema
Paywalls in AgentWallie are JSON documents. This is the core design principle: a paywall spec declares components, layout, styles, products, and behavior. The SDK renders these natively. An agent creates a paywall by posting JSON.
Schema Structure
Every paywall schema has these top-level fields:
{
"version": "1.0",
"name": "my-paywall",
"settings": { ... },
"theme": { ... },
"products": [ ... ],
"components": [ ... ]
}| Field | Type | Required | Description |
|---|---|---|---|
version | string | Yes | Schema version (currently "1.0") |
name | string | Yes | Paywall identifier |
settings | object | Yes | Presentation and behavior settings |
theme | object | No | Design tokens for colors, radius, fonts |
products | array | No | Product slot definitions |
components | array | Yes | Ordered list of UI components |
Full Example
{
"version": "1.0",
"name": "onboarding_v2",
"settings": {
"presentation": "modal",
"close_button": true,
"close_button_delay_ms": 0,
"background_color": "{{ theme.background }}",
"scroll_enabled": true,
"safe_area_insets": true
},
"theme": {
"background": "#FFFFFF",
"primary": "#007AFF",
"secondary": "#5856D6",
"text_primary": "#000000",
"text_secondary": "#6B7280",
"accent": "#34C759",
"surface": "#F2F2F7",
"corner_radius": 12,
"font_family": "system"
},
"products": [
{ "slot": "primary", "label": "Yearly" },
{ "slot": "secondary", "label": "Monthly" },
{ "slot": "tertiary", "label": "Lifetime" }
],
"components": [
{
"type": "image",
"id": "hero",
"props": {
"src": "https://cdn.example.com/hero.png",
"alt": "App preview",
"aspect_ratio": "16:9",
"fit": "cover"
},
"style": { "width": "100%", "margin_bottom": 16 }
},
{
"type": "text",
"id": "title",
"props": {
"content": "Unlock Everything",
"text_style": "title1",
"alignment": "center"
},
"style": { "color": "{{ theme.text_primary }}", "margin_bottom": 8 }
},
{
"type": "feature_list",
"id": "features",
"props": {
"items": [
{ "icon": "checkmark.circle.fill", "text": "Unlimited workouts" },
{ "icon": "checkmark.circle.fill", "text": "Advanced analytics" },
{ "icon": "checkmark.circle.fill", "text": "Personal coaching" }
],
"icon_color": "{{ theme.accent }}"
},
"style": { "margin_bottom": 24, "padding_horizontal": 24 }
},
{
"type": "product_picker",
"id": "products",
"props": {
"layout": "horizontal",
"show_savings_badge": true,
"savings_text": "Save {{ products.primary.savings_percentage }}%",
"selected_border_color": "{{ theme.primary }}"
},
"style": { "margin_bottom": 16, "padding_horizontal": 16 }
},
{
"type": "cta_button",
"id": "subscribe_button",
"props": {
"text": "Start Free Trial",
"subtitle": "{{ products.selected.trial.period }} free, then {{ products.selected.price }}/{{ products.selected.period }}",
"action": "purchase",
"product": "selected"
},
"style": {
"background_color": "{{ theme.primary }}",
"text_color": "#FFFFFF",
"corner_radius": "{{ theme.corner_radius }}",
"height": 56,
"margin_horizontal": 16,
"margin_bottom": 8
}
},
{
"type": "link_row",
"id": "legal",
"props": {
"links": [
{ "text": "Restore Purchases", "action": "restore" },
{ "text": "Terms", "action": "open_url", "url": "https://example.com/terms" },
{ "text": "Privacy", "action": "open_url", "url": "https://example.com/privacy" }
],
"separator": " · "
},
"style": { "text_color": "{{ theme.text_secondary }}", "font_size": 12, "alignment": "center", "margin_top": 8 }
}
]
}Settings Reference
| Property | Type | Description |
|---|---|---|
presentation | "modal" | "fullscreen" | "sheet" | "inline" | How the paywall is presented |
close_button | boolean | Whether to show a close/dismiss button |
close_button_delay_ms | number | Delay before close button appears (0 = immediate) |
close_button_style | "icon" | "text" | Close button rendering style. "icon" (default) shows a filled circle with X; "text" shows "x Close" as plain text. |
background_color | string | Background color (hex or theme reference) |
background_gradient | object | Gradient background for the paywall. Format: { "colors": ["#hex1", "#hex2"], "direction": "vertical" }. Takes precedence over background_color. |
scroll_enabled | boolean | Whether the paywall content scrolls |
safe_area_insets | boolean | Whether to respect safe area insets |
Theme Reference
Themes define design tokens that can be referenced throughout the paywall using Liquid template syntax ({{ theme.primary }}).
| Token | Type | Description |
|---|---|---|
background | string | Background color |
primary | string | Primary accent color |
secondary | string | Secondary accent color |
text_primary | string | Primary text color |
text_secondary | string | Secondary/muted text color |
accent | string | Accent color (e.g., for icons, badges) |
surface | string | Surface/card background color |
corner_radius | number | Default corner radius (in points) |
font_family | string | Font family ("system" for platform default) |
The theme object supports additional custom tokens via .passthrough() -- add any key-value pairs and reference them as {{ theme.your_custom_token }}.
Product Slots
Product slots provide an abstraction layer between the paywall design and actual store products. The paywall schema defines slots with display labels; the campaign configuration maps slots to real product IDs.
| Property | Type | Description |
|---|---|---|
slot | string | Slot identifier (e.g., "primary", "secondary", "tertiary") |
label | string | Display label (e.g., "Yearly", "Monthly") |
Liquid templates can reference product data: {{ products.primary.price }}, {{ products.selected.trial.period }}.
Liquid Templates
Text content and style values support Liquid template syntax for dynamic values:
{{ theme.primary }}-- Theme token reference{{ products.selected.price }}-- Selected product price{{ products.primary.savings_percentage }}-- Savings percentage{{ products.selected.trial.period }}-- Trial period text
These values are resolved at render time using product data from the App Store / Google Play and the theme definition.
Markdown Bold
Text content also supports markdown-style bold via **text**. Wrap any portion of your text in double asterisks and the SDK will render it with a bold font weight. This is useful for emphasizing prices or key phrases inline:
{ "content": "Free for 3 days. **$29.99/yr after.** Cancel anytime." }Complete Example
A production-quality dark-themed fitness paywall using 14 components across 10 types, with conditions, animations, expressions, gradients, three product slots, and a dark theme. This demonstrates most schema features working together.
{
"version": "1.0",
"name": "fitness-premium",
"settings": {
"presentation": "fullscreen",
"close_button": true,
"close_button_delay_ms": 3000,
"close_button_style": "icon",
"background_color": "{{ theme.background }}",
"background_gradient": {
"colors": ["#0D0D0D", "#1A1A2E", "#16213E"],
"direction": "vertical"
},
"scroll_enabled": true,
"safe_area_insets": true
},
"theme": {
"background": "#0D0D0D",
"primary": "#FF6B35",
"secondary": "#5E5CE6",
"text_primary": "#FFFFFF",
"text_secondary": "#A0A0A0",
"accent": "#30D158",
"surface": "#1C1C2E",
"corner_radius": 14,
"font_family": "system"
},
"products": [
{ "slot": "primary", "label": "Annual" },
{ "slot": "secondary", "label": "Monthly" },
{ "slot": "tertiary", "label": "Lifetime" }
],
"components": [
{
"type": "image",
"id": "hero-image",
"props": {
"src": "https://images.unsplash.com/photo-1534438327276-14e5300c3a48?w=800&q=80",
"alt": "Fitness hero image",
"aspect_ratio": "1.8",
"fit": "cover"
},
"style": { "margin_bottom": 0, "corner_radius": 0, "opacity": 0.7 },
"animation": { "type": "fade_in", "duration_ms": 500, "delay_ms": 0 }
},
{
"type": "badge",
"id": "limited-badge",
"props": { "text": "LIMITED TIME OFFER", "variant": "filled" },
"style": {
"background_color": "{{ theme.primary }}",
"color": "#FFFFFF",
"margin_top": 16,
"alignment": "center"
},
"animation": { "type": "bounce", "duration_ms": 400, "delay_ms": 200 }
},
{
"type": "text",
"id": "title",
"props": {
"content": "Unlock **FitnessTracker Pro**",
"text_style": "title1",
"alignment": "center"
},
"style": {
"margin_top": 12,
"margin_bottom": 4,
"color": "{{ theme.text_primary }}",
"letter_spacing": 1
},
"animation": { "type": "slide_up", "duration_ms": 400, "delay_ms": 100 }
},
{
"type": "text",
"id": "subtitle",
"props": {
"content": "Train smarter. Recover faster. Get results.",
"text_style": "subheadline",
"alignment": "center"
},
"style": { "color": "{{ theme.text_secondary }}", "margin_bottom": 16 },
"animation": { "type": "fade_in", "duration_ms": 300, "delay_ms": 200 }
},
{
"type": "countdown_timer",
"id": "urgency-timer",
"props": { "duration_seconds": 3600, "format": "hh:mm:ss" },
"style": {
"alignment": "center",
"color": "{{ theme.primary }}",
"font_size": 18,
"margin_bottom": 16
},
"animation": { "type": "fade_in", "duration_ms": 300, "delay_ms": 300 }
},
{
"type": "stack",
"id": "social-proof-row",
"props": {
"direction": "horizontal",
"spacing": 16,
"alignment": "center",
"children": [
{
"type": "rating",
"id": "app-rating",
"props": { "value": 4.8, "count": 12400, "label": "App Store", "max_stars": 5 },
"style": { "color": "#FFD700" }
},
{
"type": "badge",
"id": "users-badge",
"props": { "text": "500K+ users", "variant": "soft" },
"style": {
"background_color": "{{ theme.surface }}",
"color": "{{ theme.text_secondary }}"
}
}
]
},
"style": { "margin_bottom": 20, "padding_horizontal": 16 },
"animation": { "type": "fade_in", "duration_ms": 300, "delay_ms": 300 }
},
{
"type": "divider",
"id": "divider-top",
"props": { "color": "#2C2C2E", "thickness": 1 },
"style": { "margin_horizontal": 16, "margin_bottom": 16 }
},
{
"type": "feature_list",
"id": "features",
"props": {
"items": [
{ "icon": "flame.fill", "text": "AI-powered workout plans" },
{ "icon": "chart.line.uptrend.xyaxis", "text": "Advanced analytics & insights" },
{ "icon": "heart.text.square.fill", "text": "Heart rate zone training" },
{ "icon": "figure.run", "text": "Unlimited exercise library" },
{ "icon": "moon.fill", "text": "Sleep & recovery tracking" },
{ "icon": "fork.knife", "text": "Nutrition plans & meal tracking" }
],
"icon_color": "{{ theme.primary }}"
},
"style": { "margin_bottom": 20, "padding_horizontal": 20 },
"animation": { "type": "slide_up", "duration_ms": 400, "delay_ms": 400 }
},
{
"type": "divider",
"id": "divider-bottom",
"props": { "color": "#2C2C2E", "thickness": 1 },
"style": { "margin_horizontal": 16, "margin_bottom": 16 }
},
{
"type": "product_picker",
"id": "plans",
"props": {
"layout": "cards",
"show_savings_badge": true,
"savings_text": "Save {{ products.primary.savings_percentage }}%",
"show_price": true,
"selected_border_color": "{{ theme.primary }}"
},
"style": { "margin_bottom": 20, "padding_horizontal": 16 },
"animation": { "type": "slide_up", "duration_ms": 400, "delay_ms": 500 }
},
{
"type": "text",
"id": "trial-text",
"props": {
"content": "Start your {{ products.selected.trial_period }} free trial today",
"text_style": "footnote",
"alignment": "center"
},
"style": { "color": "{{ theme.accent }}", "margin_bottom": 12 },
"condition": {
"field": "products.selected.has_trial",
"operator": "is",
"value": true
}
},
{
"type": "cta_button",
"id": "subscribe-btn",
"props": {
"text": "Start Free Trial",
"subtitle": "then {{ products.selected.price }}{{ products.selected.period_label }}",
"action": "purchase",
"product": "selected"
},
"style": {
"background_color": "{{ theme.primary }}",
"color": "#FFFFFF",
"margin_horizontal": 16,
"margin_bottom": 8,
"padding_vertical": 18,
"corner_radius": 14,
"font_size": 18,
"glow_color": "#FF6B3540"
},
"animation": { "type": "bounce", "duration_ms": 500, "delay_ms": 600 }
},
{
"type": "text",
"id": "cancel-note",
"props": {
"content": "Cancel anytime. No questions asked.",
"text_style": "caption",
"alignment": "center"
},
"style": { "color": "{{ theme.text_secondary }}", "margin_bottom": 16 }
},
{
"type": "link_row",
"id": "footer-links",
"props": {
"links": [
{ "text": "Restore Purchases", "action": "restore" },
{ "text": "Terms of Service", "action": "open_url", "url": "https://example.com/terms" },
{ "text": "Privacy Policy", "action": "open_url", "url": "https://example.com/privacy" }
],
"separator": " | "
},
"style": { "margin_top": 8, "margin_bottom": 24 }
}
]
}This example demonstrates:
- Settings: fullscreen presentation, delayed close button, background gradient (3 color stops)
- Theme: dark color scheme with orange primary, purple secondary, green accent
- Products: 3 slots (annual, monthly, lifetime)
- 10 component types:
image,badge,text,countdown_timer,stack,rating,divider,feature_list,product_picker,cta_button,link_row - Expressions:
{{ theme.* }}for colors,{{ products.selected.* }}for dynamic pricing,{{ products.primary.savings_percentage }}for slot-specific data - Conditions: trial text only renders when
products.selected.has_trialistrue - Animations: staggered entrance with
fade_in,slide_up,bounce, using incrementingdelay_ms - Nested components:
stackwith horizontalrating+badgechildren - Markdown bold:
**FitnessTracker Pro**in the title - Glow effect: CTA button with
glow_colorfor a neon shadow
Next Steps
- Component Types -- All 19 component types with props and examples
- DSL Reference -- Conditionals, expressions, gradients, and animations
- Tap Behaviors -- All 9 tap behaviors for interactive components