Native in-app templates syntax
เนื้อหานี้ยังไม่มีในภาษาของคุณ
Native in-apps are rendered directly by the SDK. No WebView is involved. Instead of an index.html page, the ZIP resource carries a native-config.json file that describes the message as structured data (layout type, texts, colors, images, buttons). The SDK reads this file and draws the matching native view, which gives smoother animations and better performance than an embedded web page.
This guide documents the native-config.json schema: fields, types, and examples for each display type. For the classic HTML-based format, see Rich media templates syntax.
Prerequisites
Anchor link toNative in-apps require:
- iOS: SDK 7.2.0 or later (7.2.1+ for banner, carousel, and sheet)
- Android: SDK 6.10.0 or later (6.10.1+ for banner, carousel, and sheet)
Not every display type is available on both platforms yet. Check Platform support before you rely on a specific format.
Platform support
Anchor link to| Display type | iOS | Android |
|---|---|---|
| modal | ✅ 7.2.0+ | ✅ 6.10.0+ |
| fullscreen | ✅ 7.2.0+ | ✅ 6.10.0+ |
| stories | ✅ 7.2.0+ | ✅ 6.10.0+ |
| banner | ✅ 7.2.1+ | ✅ 6.10.1+ |
| carousel | ✅ 7.2.1+ | ✅ 6.10.1+ |
| sheet | ✅ 7.2.1+ | ✅ 6.10.1+ |
| video | ✅ | Not yet available |
| pip | ✅ | Not yet available |
| scratchcard | ✅ | Not yet available |
| spinwheel | ✅ | Not yet available |
Template structure
Anchor link toA native in-app template is a ZIP archive, same as a regular Rich Media template, except the root contains a native-config.json file instead of index.html:
<template>.zip├── native-config.json ← required, layout and content├── pushwoosh.json ← optional, localization (see below)Images and videos referenced from native-config.json (image, poster, fallback, url on pip/video) must be absolute HTTPS URLs. The SDK loads them over the network. It does not read local files from the archive.
The config itself is a single JSON object:
{ "displayType": "<type>", "<type>": { /* content block for this type */ } }displayType selects one of the ten formats below. The object under the matching key holds that format’s content. A config with an unknown displayType, a missing content block, or an empty required list (items for carousel/stories, segments for spinwheel) is invalid. The SDK skips showing it rather than rendering a broken layout.
Delivery settings (start/end dates and frequency capping) are not part of native-config.json. They’re configured the same way as for any other in-app, in the campaign’s Display settings step.
Frequency capping additionally needs an explicit SDK-side opt-in to take effect on native in-apps. See SDK integration.
Colors
Anchor link toEvery color value is a CSS hex string: #RGB, #RGBA, #RRGGBB, or #RRGGBBAA. The leading # is required in all four forms.
Shared building blocks
Anchor link toThese smaller objects are reused across several display types.
Text
Anchor link to| Field | Type | Required | Description |
|---|---|---|---|
text | string | yes | The text content |
color | string | yes | Text color |
{ "text": "Spin for a garage perk", "color": "#FFFFFFFF" }Border
Anchor link to| Field | Type | Required | Description |
|---|---|---|---|
color | string | yes | Border color |
radius | number | yes | Corner radius, in points |
{ "color": "#0E72E5FF", "radius": 12 }Cover
Anchor link toAn optional image over a background color. Used by fullscreen and scratchcard.
| Field | Type | Required | Description |
|---|---|---|---|
image | string | no | Cover image URL |
background | string | yes | Background color shown under (or instead of) the image |
{ "image": "https://example.com/cover.jpg", "background": "#1A1A1EFF" }Action
Anchor link toA discriminated union on type:
| Variant | Fields | Description |
|---|---|---|
{ "type": "close" } | None | Closes the in-app |
{ "type": "url", "url": string } | url required | Opens a URL or deep link |
{ "type": "url", "url": "pushwoosh://sale" }Button
Anchor link to| Field | Type | Required | Description |
|---|---|---|---|
text | Text | yes | Button label |
background | string | yes | Button fill color |
border | Border | yes | Button border |
action | Action | yes | Action fired on tap |
spinButton (spinwheel) and revealButton (scratchcard) use the same shape without action. Their behavior (spin the wheel, reveal the card) is built in.
{ "text": { "text": "Book a test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" }}Reward
Anchor link toThe prize panel shown by scratchcard and spinwheel. A valid reward has a title or a code.
| Field | Type | Required | Description |
|---|---|---|---|
title | Text | no | Reward headline |
message | Text | no | Reward description |
code | string | no | Promo code, rendered with a copy button |
button | Button | no | Confirmation button with its own action |
{ "title": { "text": "20% off detailing", "color": "#111111FF" }, "message": { "text": "Valid for any full-detail booking this month.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Book detailing", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } }}Display types
Anchor link tobanner
Anchor link toA compact bar docked to the top or bottom edge of the screen.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
position | top | bottom | yes | Screen edge |
background | string | yes | Bar background color |
image | string | no | Thumbnail on the left |
title | Text | no | Single-line title, truncated with an ellipsis |
message | Text | no | Body text, up to 2 lines |
action | Action | yes | Fired when the bar itself is tapped |
autoDismiss | number | no | Auto-close after this many seconds. Omit to keep it until closed |
{ "displayType": "banner", "banner": { "showClose": true, "position": "bottom", "background": "#4B5057FF", "image": "https://example.com/thumb.jpg", "title": { "text": "Alpine A110 just dropped", "color": "#FFFFFFFF" }, "message": { "text": "The featherweight icon — tap to see the build", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" }, "autoDismiss": 6 }}carousel
Anchor link toA full-screen, swipeable set of cards with page-indicator dots.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
items | Item[] | yes | Cards (at least 1) |
Carousel item:
| Field | Type | Required | Description |
|---|---|---|---|
title | Text | no | Card title |
message | Text | no | Card subtitle |
image | string | no | Card image |
action | Action | no | Fired when the card is tapped |
{ "displayType": "carousel", "carousel": { "showClose": true, "items": [ { "image": "https://example.com/card-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "585 hp biturbo V8 — just landed", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } }, { "image": "https://example.com/card-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "Featherweight icon — limited allocation", "color": "#FFFFFFFF" }, "action": { "type": "url", "url": "pushwoosh://product/x6f" } } ] }}fullscreen
Anchor link toAn edge-to-edge cover image with text and buttons on top.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
cover | Cover | yes | Background image and color |
title | Text | no | Title |
message | Text | no | Body text |
buttons | Button[] | yes | Buttons at the bottom (can be empty) |
{ "displayType": "fullscreen", "fullscreen": { "showClose": true, "cover": { "image": "https://example.com/hero.jpg", "background": "#1A1A1EFF" }, "title": { "text": "Pure Maranello", "color": "#FFFFFFFF" }, "message": { "text": "The prancing horse, reimagined.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Reserve now", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 8 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Not now", "color": "#FFFFFFFF" }, "background": "#00000000", "border": { "color": "#FFFFFF99", "radius": 8 }, "action": { "type": "close" } } ] }}modal
Anchor link toA centered card.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
dimBackground | boolean | yes | Dim the screen behind the card |
background | string | yes | Card background color |
image | string | no | Cover image |
title | Text | no | Title |
message | Text | no | Body text |
buttons | Button[] | yes | Buttons below the text (can be empty) |
{ "displayType": "modal", "modal": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "The GT R has landed", "color": "#4B5057FF" }, "message": { "text": "585 hp — now in the showroom.", "color": "#4B5057FF" }, "buttons": [ { "text": { "text": "Book a test drive", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } }, { "text": { "text": "Not now", "color": "#4B5057FF" }, "background": "#FFFFFFFF", "border": { "color": "#4B5057FF", "radius": 12 }, "action": { "type": "close" } } ] }}A floating picture-in-picture video window docked to a screen corner.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
position | bottom-right | bottom-left | top-right | top-left | yes | Screen corner |
loop | boolean | yes | Loop playback |
muted | boolean | yes | Start muted |
url | string | yes | Video URL |
poster | string | no | Poster shown before playback starts |
fallback | string | no | Image shown if the video fails to play |
width | number | yes | Window width as a percentage of screen width, clamped 15–70 |
aspectRatio | number | yes | Window height-to-width ratio |
borderRadius | number | no | Window corner radius, in points |
action | Action | no | Fired when the window itself is tapped |
There are no configurable buttons on pip. Window controls (expand to full screen, mute, close) are system-provided.
{ "displayType": "pip", "pip": { "showClose": true, "position": "bottom-right", "loop": true, "muted": true, "url": "https://example.com/teaser.mp4", "poster": "https://example.com/poster.jpg", "width": 40, "aspectRatio": 0.5625, "action": { "type": "url", "url": "pushwoosh://product/x6f" } }}scratchcard
Anchor link toA card with the reward hidden under a scratchable foil layer.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
background | string | string[] | yes | Card background color, or gradient stops |
revealThreshold | number | yes | Fraction of the foil that must be scratched off (0–1) before the reward reveals |
cover | Cover | yes | The foil layer. Without an image, a “scratch here” hint is shown on the background color |
revealButton | Button (no action) | no | ”Reveal instantly” button |
title | Text | no | Title |
message | Text | no | Body text |
reward | Reward | yes | The prize hidden under the foil |
{ "displayType": "scratchcard", "scratchcard": { "showClose": true, "background": ["#3A1C71FF", "#B3227CFF", "#E0503AFF"], "revealThreshold": 0.55, "cover": { "background": "#C9CDD6FF" }, "revealButton": { "text": { "text": "Reveal without scratching", "color": "#3A1C71FF" }, "background": "#F2DFF5FF", "border": { "color": "#F2DFF5FF", "radius": 10 } }, "title": { "text": "Your loyalty reward", "color": "#FFFFFFFF" }, "message": { "text": "Scratch the foil to reveal this week's garage perk.", "color": "#F2DFF5FF" }, "reward": { "title": { "text": "20% off detailing", "color": "#111111FF" }, "message": { "text": "Valid for any full-detail booking this month.", "color": "#555555FF" }, "code": "APEX20", "button": { "text": { "text": "Book detailing", "color": "#FFFFFFFF" }, "background": "#B3227CFF", "border": { "color": "#B3227CFF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://detailing" } } } }}sheet
Anchor link toA card pinned to the bottom edge, with a drag handle.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
dimBackground | boolean | yes | Dim the screen behind the sheet |
background | string | yes | Sheet background color |
image | string | no | Cover image |
title | Text | no | Title |
message | Text | no | Body text |
buttons | Button[] | yes | Buttons below the text (can be empty) |
{ "displayType": "sheet", "sheet": { "showClose": true, "dimBackground": true, "background": "#FFFFFFFF", "image": "https://example.com/cover.jpg", "title": { "text": "Your quote is ready", "color": "#000000FF" }, "message": { "text": "Guaranteed buyout for your A110: $68,500.", "color": "#000000FF" }, "buttons": [ { "text": { "text": "Get guaranteed quote", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}spinwheel
Anchor link toA wheel-of-fortune with weighted segments and a center hub button.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
background | string | string[] | yes | Card background color, or gradient stops |
winIndex | number | yes | Index (0-based) of the winning segment |
spinButton | Button (no action) | yes | Center hub button |
title | Text | no | Title |
message | Text | no | Body text |
reward | Reward | yes | Reward for the winning spin (fallback for segments without their own) |
loseTitle | Text | no | Headline shown on a loss |
segments | Segment[] | yes | Wheel segments (SDK expects 2–12) |
Segment:
| Field | Type | Required | Description |
|---|---|---|---|
message | Text | yes | Segment label |
color | string | no | Segment color. Omit for a fallback palette applied around the wheel |
weight | number | yes | Relative segment size |
reward | Reward | no | Segment-specific reward |
{ "displayType": "spinwheel", "spinwheel": { "showClose": true, "background": ["#1B1B46FF", "#5B2B8FFF", "#B0338AFF"], "winIndex": 1, "spinButton": { "text": { "text": "SPIN", "color": "#1B1B46FF" }, "background": "#F2C94CFF", "border": { "color": "#D9A02BFF", "radius": 36 } }, "title": { "text": "Spin for a garage perk", "color": "#FFFFFFFF" }, "message": { "text": "One spin — every slice wins this week.", "color": "#E3D9F2FF" }, "reward": { "title": { "text": "You won a garage perk!", "color": "#FFFFFFFF" }, "code": "APEXPERK", "button": { "text": { "text": "Claim", "color": "#FFFFFFFF" }, "background": "#5B2B8FFF", "border": { "color": "#5B2B8FFF", "radius": 12 }, "action": { "type": "close" } } }, "segments": [ { "message": { "text": "5% off", "color": "#FFFFFFFF" }, "color": "#5856D6FF", "weight": 1 }, { "message": { "text": "20% off", "color": "#FFFFFFFF" }, "color": "#30B0C7FF", "weight": 1, "reward": { "title": { "text": "20% off your next service", "color": "#FFFFFFFF" }, "code": "SPIN20", "button": { "text": { "text": "Claim service deal", "color": "#FFFFFFFF" }, "background": "#30B0C7FF", "border": { "color": "#30B0C7FF", "radius": 12 }, "action": { "type": "url", "url": "pushwoosh://service" } } } }, { "message": { "text": "Free wash", "color": "#FFFFFFFF" }, "color": "#FF2D55FF", "weight": 1 } ] }}stories
Anchor link toFull-screen slides with progress bars at the top, similar to social media stories.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
loop | boolean | yes | Restart from the first slide after the last one |
items | Item[] | yes | Slides (at least 1) |
Stories item:
| Field | Type | Required | Description |
|---|---|---|---|
title | Text | no | Title |
message | Text | no | Subtitle |
image | string | no | Slide background image |
buttons | Button[] | yes | CTA buttons at the bottom (can be empty) |
duration | number | yes | Slide duration, in seconds |
{ "displayType": "stories", "stories": { "showClose": true, "loop": false, "items": [ { "image": "https://example.com/slide-1.jpg", "title": { "text": "AMG GT R", "color": "#FFFFFFFF" }, "message": { "text": "The Green Hell special", "color": "#FFFFFFFF" }, "buttons": [ { "text": { "text": "Configure yours", "color": "#FFFFFFFF" }, "background": "#0F0F0FFF", "border": { "color": "#0F0F0FFF", "radius": 26 }, "action": { "type": "url", "url": "pushwoosh://product/n6fx" } } ], "duration": 4 }, { "image": "https://example.com/slide-2.jpg", "title": { "text": "Alpine A110", "color": "#FFFFFFFF" }, "message": { "text": "The featherweight legend, reborn", "color": "#FFFFFFFF" }, "buttons": [], "duration": 4 } ] }}video
Anchor link toFull-screen video with text and buttons on top.
| Field | Type | Required | Description |
|---|---|---|---|
showClose | boolean | yes | Show a close (✕) button |
loop | boolean | yes | Loop playback |
muted | boolean | yes | Start muted |
url | string | yes | Video URL (HLS or MP4) |
poster | string | no | Poster shown before playback starts |
fallback | string | no | Image shown if the video fails to play |
title | Text | no | Title |
message | Text | no | Body text |
buttons | Button[] | yes | CTA buttons at the bottom (can be empty) |
{ "displayType": "video", "video": { "showClose": true, "loop": true, "muted": true, "url": "https://example.com/reveal.mp4", "poster": "https://example.com/poster.jpg", "title": { "text": "The reveal", "color": "#FFFFFFFF" }, "message": { "text": "Watch it move before anyone else.", "color": "#EBEBEBFF" }, "buttons": [ { "text": { "text": "Shop the lineup", "color": "#FFFFFFFF" }, "background": "#0E72E5FF", "border": { "color": "#0E72E5FF", "radius": 14 }, "action": { "type": "url", "url": "pushwoosh://sale" } } ] }}Localization
Anchor link toNative in-apps reuse the exact same localization mechanism as HTML Rich Media: string values in native-config.json can carry {{key|type|default}} placeholders, and translations live in a pushwoosh.json file next to it, in the same format described in Adding pushwoosh.json. A placeholder can appear in any string field, at any depth (a title, a button label, an image URL, an action URL).
SDK integration
Anchor link toOnce you add the native in-app SDK module to your app, messages display automatically. No extra code is required to show messages triggered by a push, Customer Journey, postEvent, or the inbox.
The SDK also exposes a small API for manual control:
- iOS:
Pushwoosh.inApp(modulePushwooshInApp) - Android:
PushwooshInAppUi(modulepushwoosh-inapp-ui)
| Capability | iOS | Android |
|---|---|---|
| Show a config directly (testing/manual use) | Pushwoosh.inApp.present(config) | PushwooshInAppUi.present(configJson) |
| Observe lifecycle and clicks | delegate (PWInAppMessageDelegate) | delegate (InAppMessageDelegate) |
| Check if something is on screen | isPresenting | isPresenting |
| Dismiss whatever is currently shown | dismiss() | dismiss() |
| Pause / resume display | isPaused | isPaused |
Enforce maxDisplays / cooldown capping | setFrequencyCapEnabled(_:) | setFrequencyCapEnabled(...) |
Delegate callbacks (all fired on the main thread): shouldDisplay (return false to suppress one message before it shows, for example on a checkout screen), willPresent, didPresent, didClose, and clickedAction (fired when the user taps a url action, before the URL opens).
iOS additionally reports rewardRevealed and rewardClaimed for the gamified scratchcard and spinwheel templates.
// iOSPushwoosh.inApp.delegate = selfPushwoosh.inApp.setFrequencyCapEnabled(true)// AndroidPushwooshInAppUi.delegate = thisPushwooshInAppUi.setFrequencyCapEnabled(true)