
The in-app template builder allows you to:
- create in-app message templates from scratch and edit them by using HTML, CSS, and JavaScript  
    
  <div class="admonition admonition-important"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

  The `SRInApp.close()` method must be included in every in-app message.

  </div></div></div>

- use the ready-made templates from the **Predefined templates** folder.

The **Predefined templates** folder provides you with templates for the most common campaign scenarios such as sending an abandoned cart, displaying a carousel with item recommendations, and displaying a simple banner with a button. 

Modification of the ready-made templates doesn't require applying changes to the template code. The template builder contains a user-friendly configuration form that brings editing down to filling out fields that define the properties of the template. This makes editing templates possible by any user regardless of the programming skills. You can [simplify the editing of your own templates as well](#template-editing-simplification) by creating custom configuration form adjusted to your needs. Thanks to this you can edit your template or create its variations dedicated for different scenarios.

You can personalize the content of the message by [using snippets](#adding-a-snippet-to-the-template-code) and [Jinjava inserts](/developers/inserts) to inject data such as profile attributes (for example, name), recommendations, analysis results.

[Snippets](#adding-a-snippet-to-the-template-code) also let you re-use the same content in multiple templates, or create a reference to a fragment that you only need to update in one place to see the change in all templates where it's used.

## Character limits

The template to be used in an in-app message campaign cannot exceed 60,000 characters.

## Good practices

### Campaign planning recommendations

Using a large number of in-app messages in your application can impact rendering time, message delivery, and battery usage.

To maintain optimal application performance when using in-app campaigns:
- Avoid assigning more than 10 in-app messages to the same trigger event.
- Avoid having more than 20 in-app messages active at the same time in your application.
- Review and archive in-app campaigns that you no longer need.  

### Template construction

When creating or editing in-app message content:

- Place the the `SRInApp.close()` (or `SRInApp.hide()`) method at the beginning of the JS script.
- Use try/catch to handle possible fatal errors in the JS script.
- Handle situations where Jinjava inserts return empty data.
- When adding external links to your message:
    - Only link to sites you trust.
    - Don't link to large images that may negatively affect performance.
    - Don't link to resources whose CSS/HTML may be blocked. If you have resources loaded from your own URLs, set `Synerise.settings.inAppMessaging.contentBaseUrl` and use relative paths in HTML/CSS.

## Editing a ready-made template
---

1. Go to <img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/icons/experience-hub-icon.svg" alt="Image presents the Experience Hub icon" class="icon"> **Experience Hub > In-app messages**. 
2. On the left pane, select **Templates**.  
3. From the list of template folders, select **Predefined templates**. 
4. Select one of the templates to edit. 
    - [Abandoned cart](/use-cases/abandoned-basket-inapp)
    - Bottom bar
    - [Carousel with context recommendations](/use-cases/in-app-cross-sell)
    - [Carousel with recommendation campaign](/use-cases/in-app-recommendations)
    - [Fullscreen](/use-cases/inapp-mandatory-upgrade)
    - Modal  
    - [Price alert](/use-cases/in-app-price-drop-last-seen-products) 
    - [Swiping mechanism with recommendation campaign](/use-cases/in-app-swiping-mechanism)
    - Top bar
    - [Walkthrough](/use-cases/inapp-walkthrough)  
  **Result**: You are redirected to the code editor.
4. You can edit the template in two ways:
    - Edit the code of the template ([add inserts](#adding-a-snippet-to-the-template-code), [add variables](#adding-a-variable)).  
        The code of the template is embedded in `<body` when the message is displayed, so it can't include the `<html>` or `<head>` elements.
    - Go to the **Config** tab and fill out the form.
5. After you make changes to the template, you can check the [preview](#previewing-templates).  
6. If the template is ready, in the upper right corner click **Save this template > Save as**. 
7. On the pop-up:  
    1. In the **Template name** field, enter the name of the template.  
    2. From the **Template folder** dropdown list, select the folder where the template will be saved.  
    3. Confirm by clicking **Apply**.  


## Creating a template
---

1. Go to <img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/icons/experience-hub-icon.svg" alt="Image presents the Experience Hub icon" class="icon"> **Experience Hub > In-app messages**. 
2. On the left pane, select **Templates**.  
3. In the upper right corner, click **New Template**.
4. Use the **HTML**, **CSS**, and **JavaScript** tabs to define the properties of the template.  
    The code of the template is embedded in `<body` when the message is displayed, so it can't include the `<html>` or `<head>` elements.
6. If the template is ready, in the upper right corner click **Save this template > Save as**. 
7. On the pop-up:  
    1. In the **Template name** field, enter the name of the template.  
    2. From the **Template folder** dropdown list, select the folder where the template will be saved.  
    3. Confirm by clicking **Apply**.  


### Selecting in-app message type

To select the way of displaying your in-app message within a mobile application:

1. On the part of the screen with an in-app message preview, click **Display type**.
2. From the dropdown list, select one of the following options:
    - **Fullscreen** - In-app messages are displayed in the center of the mobile application, with no clickable elements within the application.
    - **Top bar** - In-app messages are shown at the top bar of the application, and user interaction is limited to the area below the message.
    - **Bottom bar** - In-app messages appear at the bottom bar of the application, with user interaction restricted to the area above the message.
3. If you want to manage the display of in-app messages over safe area in your mobile application, use the **Cover safe area** option.  
    A safe area is the portion of a view that is not covered by elements such as a navigation bar, tab bar, or toolbar. Safe areas are important for ensuring visibility and access to a device's interactive features.  
    
   <div class="admonition admonition-important"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z" /></svg></div><div class="admonition-body"><div class="admonition-content">

   Available from the following application versions:  
   - iOS: `5.1.0` and higher
   - Android: `6.1.0` and higher

   </div></div></div>


    - By enabling this option, the in-app message will extend into bars, notches and other UI elements.
    - By disabling this option, the in-app message won't cover system UI elements such as top bars, notches, and so on. This is the default setting.


<figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/in-app-code-builder-display-type.png" class="full" alt="View of the Display type option in the template builder"><figcaption>View of the Display type option in the template builder</figcaption></figure>

### Adding a snippet to the template code

[Snippets](/docs/assets/snippets) let you:
- insert data such as profile attribute, recommendations, or analysis results into the communication.
- create re-usable pieces of static content, so you don't need to manually copy and paste between templates.
- create dynamic pieces of content that are updated in all templates when you update the snippet definition.

1. Click **Snippets**.  
    **Result**: The snippet widget opens.  
2. Add a snippet as described in [Snippets](/docs/assets/snippets).




## JavaScript methods in in-app messages

JavaScript methods can be used to let the SDK Listeners and Delegates [handle actions from in-app messaging campaigns](/developers/mobile-sdk/campaigns/action-handling#handling-actions-from-campaigns). The methods only work when the Listeners/Delegates are implemented and running.

Every message must include a method to close or hide the message.

### Use a Mobile SDK method

You can use the `SRInApp.internalMethod` method to call Synerise Mobile SDK methods from the JS code in an in-app. This simplifies using those methods, without creating JS code to authorize and make API requests.

**Syntax**:  
```
SRInApp.internalMethod(String methodName, String arguments, onSuccess(String response), onError(Any error))
```
where:
- `methodName` is the mobile SDK method. See table below for available methods.
- `arguments` is a stringified JSON object with the method arguments, described in the method reference for each mobile SDK method. Some methods don't require any arguments.
- `onSuccess` is the function to call when the method succeeds.  
    In its arguments, `response` is a stringified response from the mobile SDK method. The structure depends on the mobile SDK method.
- `onError` is the function to call when the method fails.

| `methodName`                           | Corresponding SDK method                                                                                                                                                                                                                                                           |
| -------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `Promotions/getPromotionsWithApiQuery` | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#get-all-promotions-of-a-customer">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#get-promotions-with-query-parameters">iOS</a></li><ul>           | | `Promotions/getPromotionByUuid`                  | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#get-promotion-by-uuid">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#get-promotion-by-uuid">iOS</a></li><ul>                                     | | `Promotions/getPromotionByCode`                  | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#get-promotion-by-code">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#get-promotion-by-code">iOS</a></li><ul>                                     | | `Promotions/activatePromotionByUuid`             | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#activate-promotion-by-uuid">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#activate-promotion-by-uuid">iOS</a></li><ul>                           | | `Promotions/activatePromotionByCode`             | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#activate-promotion-by-code">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#activate-promotion-by-code">iOS</a></li><ul>                           | | `Promotions/deactivatePromotionByUuid`           | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#deactivate-promotion-by-uuid">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#deactivate-promotion-by-uuid">iOS</a></li><ul>                       | | `Promotions/deactivatePromotionByCode`           | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#deactivate-promotion-by-code">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#deactivate-promotion-by-code">iOS</a></li><ul>                       | | `Promotions/activatePromotionsBatch`           | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#activate-promotions-in-a-batch">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#activate-promotions-in-a-batch">iOS</a></li><ul>                   | | `Promotions/deactivatePromotionsBatch`         | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#deactivate-promotions-in-a-batch">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#deactivate-promotions-in-a-batch">iOS</a></li><ul>               | | `Vouchers/getOrAssignVoucher`            | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#get-or-assign-voucher-from-pool">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#get-or-assign-voucher-from-pool">iOS</a></li><ul>                 | | `Vouchers/assignVoucherCode`                    | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#assign-voucher-code-from-pool">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#assign-voucher-code-from-pool">iOS</a></li><ul>                     | | `Vouchers/getAssignedVoucherCodes`             | <ul><li><a href="/developers/mobile-sdk/method-reference/android/promotions/#get-voucher-codes-assigned-to-customer">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/promotions/#get-voucher-codes-assigned-to-customer">iOS</a></li><ul>   | | `Content/getRecommendationsV2`                    | <ul><li><a href="/developers/mobile-sdk/method-reference/android/content/#get-recommendations-v2">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/content/#get-recommendations-v2">iOS</a></li><ul>                                         | | `Content/generateDocument`                      | <ul><li><a href="/developers/mobile-sdk/method-reference/android/content/#generate-document-with-query-parameters">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/content/#generate-document-with-query-parameters">iOS</a></li><ul>       | | `Content/generateScreenView`                    | <ul><li><a href="/developers/mobile-sdk/method-reference/android/content/#generate-screen-view-with-query-parameters">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/content/#generate-screen-view-with-query-parameters">iOS</a></li><ul> | | `Content/generateBrickworks` | <ul><li><a href="/developers/mobile-sdk/method-reference/android/content/#generate-brickworks">Android</a></li></li><a href="/developers/mobile-sdk/method-reference/ios/content/#generate-brickworks">iOS</a></li></ul> | | `Client/getUuid`                               | <ul><li><a href="/developers/mobile-sdk/method-reference/android/client-session/#get-current-customer-uuid">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/client-session/#get-current-customer-uuid">iOS</a></li><ul>                     | | `Client/destroySession` | <ul><li><a href="/developers/mobile-sdk/method-reference/android/client-session/#destroy-current-session">Android</a></li><li><a href="/developers/mobile-sdk/method-reference/ios/client-session/#destroy-current-session">iOS</a></li><ul> | **Example**:   This is how you can activate a promotion when a button is clicked: <pre><code class="language-js">function internalMethodOnSuccess () { console.log("ACTIVATED"); }; function internalMethodOnError () { console.log("FAILED TO ACTIVATE"); }; (function () { const params = { uuid: '7cdc22d5-cf8c-4102-a853-a9f00d0256b0' }; var button = document.querySelector(".in-app-button"); button.addEventListener("click", function () { SRInApp.internalMethod("Promotions/activatePromotionByUuid", JSON.stringify(params), internalMethodOnSuccess, internalMethodOnError) }); })();</code></pre> ### Close a message This method sends an [inApp.discard](/docs/assets/events/event-reference/inapp#inappdiscard) event when closing the in-app. ``` SRInApp.close() ``` The method has no parameters. <div class="admonition admonition-tip"><div class="admonition-icon"><svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke="currentColor" stroke-width="2.5"><path stroke-linecap="round" stroke-linejoin="round" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z" /></svg></div><div class="admonition-body"><div class="admonition-content"> The mobile SDK allows closing in-app messages from outside the message. See [Mobile SDK documentation](/developers/mobile-sdk/campaigns/in-app-message#closing-a-message). </div></div></div> ### Close message and send an event This method sends an additional event ([inApp.discard](/docs/assets/events/event-reference/inapp#inappdiscard) is sent automatically) when closing the in-app. ``` SRInApp.closeAndTrigger(action,params,label) ``` - `action` is the event's action name (string) in the `context.action` format, for example `page.visit`. The maximum length is 32 characters. - `params` is an object with free-form JSON parameters to send with the event. - `label` is a string with a human-readable summary of the event. It is obligatory, but not saved in persistent storage and can't be used in Decision or Automation Hubs and is not displayed in **Behavioral Data Hub > Profiles**. ### Hide a message This method sends an [inApp.hide](/docs/assets/events/event-reference/inapp#inapphide) event when hiding the in-app. ``` SRInApp.hide() ``` The method has no parameters. ### Open URL This method sends an [inApp.click](/docs/assets/events/event-reference/inapp#inappclick) event when a customer opens an URL by clicking a link in the message. ``` SRInApp.openUrl(url) ``` `url` is the address to open (string). ### Open deeplink This method sends an [inApp.click](/docs/assets/events/event-reference/inapp#inappclick) event when a customer clicks a link (deeplink). ``` SRInApp.openDeeplink(deeplink) ``` `deeplink` is the deeplink to open (string). ### Resize a message This method resizes the message. ``` SRInApp.resize(resizeTarget, function) ``` where: - `resizeTarget` informs the mobile SDK about the resize type: - `FULLSCREEN` - `TOP_BAR` - `BOTTOM_BAR` - `function` is the name of the JS function that performs the resize on the device. When the method is triggered: 1. The `resizeTarget` is sent to the mobile SDK. 2. The mobile SDK returns information about the width and height of the device's screen. 3. The `function` is triggered with parameters received from the mobile SDK and the message size changes. **Example**: <div class="content-tabs" data-tab-group="tabgrp-1305"> <div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1305-0" data-tab-group="tabgrp-1305" data-tab-active="true">HTML content</button><button class="tab-button" data-tab-id="tabgrp-1305-1" data-tab-group="tabgrp-1305">JavaScript</button></div> <div class="tab-panel" data-tab-id="tabgrp-1305-0" data-tab-group="tabgrp-1305" data-tab-active="true"> <pre><code class="language-html">&lt;div id="webview-simulation" class="bottom-bar"&gt; &lt;div id="message-content"&gt; &lt;button onclick="SRInApp.resize('FULLSCREEN', resizeCallbackFull)"&gt;to full&lt;/button&gt; &lt;button onclick="SRInApp.resize('TOP_BAR', resizeCallbackTop)"&gt;to top&lt;/button&gt; &lt;button onclick="SRInApp.resize('BOTTOM_BAR', resizeCallbackBottom)"&gt;to bottom&lt;/button&gt; &lt;button onclick="SRInApp.close()"&gt;CLOSE&lt;/button&gt; &lt;p&gt; Example message &lt;/p&gt; &lt;/div&gt; &lt;/div&gt;</code></pre> </div> <div class="tab-panel" data-tab-id="tabgrp-1305-1" data-tab-group="tabgrp-1305"> <pre><code class="language-js">function resizeCallbackFull (width, height) { const container = document.getElementById('webview-simulation'); container.classList.remove('bottom-bar'); container.classList.remove('top-bar'); container.classList.add('full-screen'); container.style.height = height + 'px'; console.log("Changed to fullscreen"); }; function resizeCallbackTop (width, height) { const container = document.getElementById('webview-simulation'); container.classList.remove('full-screen'); container.classList.remove('bottom-bar'); container.classList.add('top-bar'); container.style.height = '250px'; console.log("Changed to top bar"); }; function resizeCallbackBottom (width, height) { const container = document.getElementById('webview-simulation'); container.classList.remove('full-screen'); container.classList.remove('top-bar'); container.classList.add('bottom-bar'); container.style.height = '250px'; console.log("Changed to bottom bar"); };</code></pre> </div> </div> ### Send a custom event This method sends a custom event. ``` SRInApp.trackCustomEvent(action, params, label) ``` - `action` is the event's action name (string) in the `context.action` format, for example `page.visit`. The maximum length is 32 characters. - `params` is an object with free-form JSON parameters to send with the event. - `label` is a string with a human-readable summary of the event. It is obligatory, but not saved in persistent storage and can't be used in Decision Hub and is not displayed in Behavioral Data Hub. ### In-app storage You can store and retrieve JSON data (a key/value pair) on the device. The data is assigned to a profile and can only be accessed by that profile, so you can keep separate data sets between users of the same device. Thanks to this storage, you can enrich your in-app scenarios, for example: - If the user has already seen a story, hide it or display an alternative layout or version. - Resume the story from the exact point where the user previously stopped watching. - Show different content on the first view versus subsequent views (e.g. intro vs. summary). - Track completed vs. partially viewed stories and adjust messaging accordingly. - Personalize call-to-action buttons based on prior interactions (e.g. “Continue” vs. “Watch again”). You can use these methods: - [Save and get data](#save-and-get-data) - [Delete data](#delete-data-one-value) - [Delete all data](#delete-all-data) #### Save and get data You can save primitive data types and complex types, such as objects. Each key/value pair is saved separately and expires after 90 days. You can reset the expiration counter by resending the same key. - The `saveData` method is asynchronous and may include a callback. - The `getData` method is synchronous. If the requested value doesn't exist, it returns `undefined` <div class="content-tabs" data-tab-group="tabgrp-1306"> <div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1306-0" data-tab-group="tabgrp-1306" data-tab-active="true">Primitive values</button><button class="tab-button" data-tab-id="tabgrp-1306-1" data-tab-group="tabgrp-1306">Complex values</button><button class="tab-button" data-tab-id="tabgrp-1306-2" data-tab-group="tabgrp-1306">Save + callback</button><button class="tab-button" data-tab-id="tabgrp-1306-3" data-tab-group="tabgrp-1306">Get + error handling</button></div> <div class="tab-panel" data-tab-id="tabgrp-1306-0" data-tab-group="tabgrp-1306" data-tab-active="true"> <pre><code class="language-js">SRInApp.storage.saveData('myString', 'Hello World'); const exampleString = SRInApp.storage.getData(myString); console.log(exampleString); // "Hello World" SRInApp.storage.saveData('myInt', 42); const exampleInt = SRInApp.storage.getData(myInt); console.log(exampleInt); // 42 SRInApp.storage.saveData('myFloat', 3.14159); const exampleFloat = SRInApp.storage.getData(myFloat); console.log(exampleFloat); // 3.14159 SRInApp.storage.saveData('myBool', true); const exampleBool = SRInApp.storage.getData(myBool); console.log(exampleBool); // true SRInApp.storage.saveData('myNull', null); const exampleNull = SRInApp.storage.getData(myNull); console.log(exampleNull); // null</code></pre> </div> <div class="tab-panel" data-tab-id="tabgrp-1306-1" data-tab-group="tabgrp-1306"> <pre><code class="language-js">// Arrays SRInApp.storage.saveData('myArray', [1, 2, 3, 4, 5]); const exampleArray = SRInApp.storage.getData(myArray); console.log(exampleArray); // [1,2,3,4,5] SRInApp.storage.saveData('mixedArray', ['a', 1, true, null]); const exampleMixed = SRInApp.storage.getData(mixedArray); console.log(exampleMixed); // ["a",1,true,null] // Objects SRInApp.storage.saveData('user', { name: 'John', age: 30, active: true }); const exampleObject = SRInApp.storage.getData(user); console.log(exampleObject.name);   // "John" console.log(exampleObject.age);    // 30 // Nested objects SRInApp.storage.saveData('nested', { userData: { profile: { firstName: 'Jane', lastName: 'Doe' }, settings: { darkMode: true, notifications: ['email', 'push'] } }, timestamp: 1234567890 }); const exampleNested = SRInApp.storage.getData(nested); console.log(exampleNested.userData.profile.firstName); // "Jane"</code></pre> </div> <div class="tab-panel" data-tab-id="tabgrp-1306-2" data-tab-group="tabgrp-1306"> In the `saveData` method, you can include a callback. <pre><code class="language-js">SRInApp.storage.saveData('preferences', { theme: 'dark', lang: 'en' }, function() { console.log('Preferences saved successfully!'); // Continue with next action... }, function(error) { console.log('Failed to save preferences:', error); } );</code></pre> </div> <div class="tab-panel" data-tab-id="tabgrp-1306-3" data-tab-group="tabgrp-1306"> You can add logic to handle non-existing keys. <pre><code class="language-js">const data = SRInApp.storage.retrieveData('nonExistentKey'); if (data === null) { console.log('No data found');</code></pre> </div> </div> #### Delete data (one value) You can delete a single key from the storage, with an optional callback: <pre><code class="language-js">// Simple delete SRInApp.storage.deleteData('userName'); // Delete with callback SRInApp.storage.deleteData('user', function() { console.log('User data deleted!'); }, function(error) { console.log('Delete failed:', error); } );</code></pre> #### Delete all data You can wipe all data from the storage assigned to the current profile, with an optional callback. <pre><code class="language-js">// Simple delete all data of current user SRInApp.storage.deleteAllData(); // Delete all storage for current user, with callback SRInApp.storage.deleteAllData( function() { console.log('All data cleared!'); }, function(error) { console.log('Clear failed:', error); } );</code></pre> ### Trigger a custom action This method allows you to pass data to a custom action. You must create logic in your mobile application to perform the action, which is triggered when a Listener/Callback processes the JS method and returns the name and parameters of the custom action. The method generates an [inApp.customHook](/docs/assets/events/event-reference/inapp#inappcustomhook) event. ``` SRInApp.handleCustomAction(name, params) ``` - `name` is used as the identifier that triggers a logic associated with it. - `params` is an object with free-form JSON parameters to pass to the logic. ### Get device information You can retrieve some details of the device as a JSON object. ``` SRInApp.getDeviceData() ``` This returns the following object: <pre><code class="language-json">{ "deviceData": { "os": "string", "osVersion": "string", "appVersion: "string", "darkMode": boolean, "language": "string", "deviceWidth": number, "deviceHeight": number } }</code></pre> You can use data from this object in your JavaScript. ## Template editing simplification To make your template more accessible to users without programming skills, you can add a configuration form with variables dedicated for the template, so the user can make adjustments to the template.   The effect of template editing simplification is that you can edit templates by filling out a user-friendly configuration form (available in the **Config** tab) whose fields define the value for each property of the template. The process of template simplification involves replacing values with variables in the HTML, CSS, and JavaScript code elements, such as alignment, font, color in CSS, or HTML tags as title, description or buttons. You can also add a variable in the place of Jinjava elements, such as a recommendation campaign ID, voucher pool ID, or catalog name. Variables inserted in the code appear in a form the **Config** tab when editing the template. #### List of variables | Variable name          | Description                                                                                                                              | Example output |
|------------------------|------------------------------------------------------------------------------------------------------------------------------------------|----------------|
| **Synerise insert select** | Allows you to add a dropdown list with aggregates, expressions, metrics, voucher pools, recommendations, files, catalogs, or attributes. | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-synerise-object-example.png" class="full" alt="A field in the Config tab requiring a selection of a Synerise object"><figcaption>Example: selecting a Synerise object from the list</figcaption></figure> |
| **String**                 | Allows you to add a field that requires a string value.                                                                                  | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-string-example.png" class="full" alt="A field in the Config tab requiring a string value"><figcaption>Example: Filling out a field</figcaption></figure>  |
| **Select**                 | Allows you to add a dropdown list with configurable values.                                                                              | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-select-example.png" class="full" alt="A field in the Config tab requiring a selection from a dropdown"><figcaption>Example: selecting an option from a dropdown list </figcaption></figure>  |
| **Switch**                 | Allows you to add a field which is enabled/disabled by a toggle.                                                                         | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-switch-example.png" class="full" alt="A field in the Config tab requiring a selection of a Synerise object"><figcaption>Example: enabling an option </figcaption></figure>   |
| **Color**                  | Allows you to add a color selector. You can either select a color or enter its code manually.                                            | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-color-example.png" class="full" alt="A field in the Config tab requiring a selection of a color"><figcaption>Example: selecting a color </figcaption></figure>  |
| **Number**                 | Allows you to add a field that requires a number. You can either select a number from a dropdown or enter it manually.                   | <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/variable-number-example.png" class="full" alt="A field in the Config tab requiring a number value"><figcaption>Example: defining a number </figcaption></figure>  |

#### Results of simplifying template editing

Instead of modifying the design of the template directly in the code, a user can go to the **Config** tab and define the properties of the template by filling out configuration form.

The image below presents the easy-to-edit form that lets users without coding expertise change the variable values:

<figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/Mobile/_gfx/mobile-config-form.png" class="full" alt="Defining the settings of a string variable"><figcaption>Defining the settings of a string variable</figcaption></figure>

### Adding a variable

1. Select one of the code editor tabs.  
    The tabs may be **JSON**, **HTML**, **CSS**, and **JavaScript**, depending on the communication type.
2. Position the cursor in the place where you want to add the variable.
3. On the right side, click **+ Variable**.  
    **Result**: A sidebar appears.
4. In the **Identifier** field, enter the ID of the variable.  
    This will be the title of the field unless you define the **Label** field.  
    The first character of the ID can't be a number.
5. From the **Type** dropdown list, select the type of variable.  
    
   <div class="content-tabs" data-tab-group="tabgrp-1307">
   <div class="tab-buttons"><button class="tab-button" data-tab-id="tabgrp-1307-0" data-tab-group="tabgrp-1307" data-tab-active="true">String</button><button class="tab-button" data-tab-id="tabgrp-1307-1" data-tab-group="tabgrp-1307">Select</button><button class="tab-button" data-tab-id="tabgrp-1307-2" data-tab-group="tabgrp-1307">Synerise insert</button><button class="tab-button" data-tab-id="tabgrp-1307-3" data-tab-group="tabgrp-1307">Switch</button><button class="tab-button" data-tab-id="tabgrp-1307-4" data-tab-group="tabgrp-1307">Color</button><button class="tab-button" data-tab-id="tabgrp-1307-5" data-tab-group="tabgrp-1307">Number</button></div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-0" data-tab-group="tabgrp-1307" data-tab-active="true">

   Allows you to add a field that requires a string value.  

   1. In the **Label (Optional)** field, enter the name of the field.  
     If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation of the field's purpose.
   3. In the **Default Value** field, enter the default value.

   </div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-1" data-tab-group="tabgrp-1307">

   Allows you to add a dropdown list with configurable values.  

   1. In the **Label (Optional)** field, enter the name of the field. If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation what this field is for.
   3. In the **Display Name** field, enter the name that will be visible in a dropdown.
   4. In the **Value** field, enter a value.
   5. In the **Default Value** field, enter the default value.

   <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/Mobile/_gfx/mobile-json-raw.png" class="full" alt="A select variable"><figcaption>A select variable during configuration </figcaption></figure>

   </div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-2" data-tab-group="tabgrp-1307">

   Allows you to add a dropdown list with aggregates, expressions, metrics, voucher pools, recommendations, files, catalogs, and attributes.  

   1. In the **Label (Optional)** field, enter the name of the field. If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation what this field is for.
   3. From the **Insert Type** dropdown list, select the type of resource:
       - **Aggregates, AI recommendations, Expressions, Metrics, Voucher pools**: creates a dropdown list of available resources of the selected type. When the user selects a resource in the form, its ID is inserted into the code of the template. This ID can be used in [Jinjava](/developers/inserts/insert-usage) to display the value of the selected resource.
       - **Catalogs**: creates a dropdown list of catalogs. When the user selects a catalog in the form, its name is inserted into the code of the template. This name can be used in [Jinjava](/developers/inserts/insert-usage#catalogs) to retrieve a value from the catalog.
       - **Files**: creates a dropdown list of [files](/docs/assets/files-explorer). When user selects a file, its URL is inserted into the code.
       - **Profile attributes**: creates a dropdown list of profile attributes. When a user selects an attribute, its name is inserted into the code of the template. This name can be used in [Jinjava](/developers/inserts/insert-usage#customer-attributes) to retrieve the attribute value.
   3. In the **Default Value (Optional)** field, enter the default value.  
       **Result**: A dropdown with the insert is added to the form in the **Config** tab. From the dropdown list, you can select an item of the chosen type (for example, aggregates). As a result, the value of variable will be ID of the selected item.
       <figure><img src="/api/docs/image/54176ad07f146575310749eba44b7c2f42c1b327/docs/campaign/_gfx/synerise-insert-result.png" class="full" alt="Synerise insert select in the configuration form"><figcaption>Synerise insert select in the configuration form</figcaption></figure>

   </div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-3" data-tab-group="tabgrp-1307">

   Allows you to add a field which is enabled/disabled by a toggle.  

   1. In the **Label (Optional)** field, enter the name of the field. If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation what this field is for.
   3. In the **Default Value** field, select the default value (true/false).

   </div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-4" data-tab-group="tabgrp-1307">

   Allows you to add a color selector. You can either select a color or enter its code manually.  

   1. In the **Label (Optional)** field, enter the name of the field. If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation what this field is for.
   3. In the **Default Value** field, enter the default value.

   </div>

   <div class="tab-panel" data-tab-id="tabgrp-1307-5" data-tab-group="tabgrp-1307">

   Allows you to add a field that requires a number. You can either select a number from a dropdown or enter it manually.  

   1. In the **Label (Optional)** field, enter the name of the field. If this field is empty, the name of the field will be taken from the **Identifier** field.  
   2. In the **Description (Optional)** field, enter a short explanation what this field is for.
   3. In the **Default Value** field, enter the default value.

   </div>
   </div>


    
6. If you want to add the variable to a group that can be more easily displayed together in the form:  
    1. Click **Variable Group**.
    2. Select or create a group:
        - To select a group, click its name.
        - To create a group:
            1. Click **Add new group**.
            2. Enter a group name.
            3. Enter a group ID.
            4. Click **Apply**.  
    **Result**: On the **Config** tab, the groups can be collapsed and expanded.
5. In the upper right corner, click **Add**.  
   **Result**: In the template code, a variable appears (it starts with `####`). It also becomes available on the **Config** tab.  
6. Optionally, to modify the order of variables appearing in the configuration form, add the `order` parameter to the variable formula (for example, `#### type: "string", id: "string", label: "Text", order: 1 !####`).

## Previewing templates

5. To check the preview of the template for a particular customer or a product, click the **Preview** button on the upper left side. 
1. Enter the ID of a customer or a product. 
2. Click **Apply**.

