Paywall Schema
Overview

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": [ ... ]
}
FieldTypeRequiredDescription
versionstringYesSchema version (currently "1.0")
namestringYesPaywall identifier
settingsobjectYesPresentation and behavior settings
themeobjectNoDesign tokens for colors, radius, fonts
productsarrayNoProduct slot definitions
componentsarrayYesOrdered 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

PropertyTypeDescription
presentation"modal" | "fullscreen" | "sheet" | "inline"How the paywall is presented
close_buttonbooleanWhether to show a close/dismiss button
close_button_delay_msnumberDelay 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_colorstringBackground color (hex or theme reference)
background_gradientobjectGradient background for the paywall. Format: { "colors": ["#hex1", "#hex2"], "direction": "vertical" }. Takes precedence over background_color.
scroll_enabledbooleanWhether the paywall content scrolls
safe_area_insetsbooleanWhether to respect safe area insets

Theme Reference

Themes define design tokens that can be referenced throughout the paywall using Liquid template syntax ({{ theme.primary }}).

TokenTypeDescription
backgroundstringBackground color
primarystringPrimary accent color
secondarystringSecondary accent color
text_primarystringPrimary text color
text_secondarystringSecondary/muted text color
accentstringAccent color (e.g., for icons, badges)
surfacestringSurface/card background color
corner_radiusnumberDefault corner radius (in points)
font_familystringFont 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.

PropertyTypeDescription
slotstringSlot identifier (e.g., "primary", "secondary", "tertiary")
labelstringDisplay 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_trial is true
  • Animations: staggered entrance with fade_in, slide_up, bounce, using incrementing delay_ms
  • Nested components: stack with horizontal rating + badge children
  • Markdown bold: **FitnessTracker Pro** in the title
  • Glow effect: CTA button with glow_color for a neon shadow

Next Steps