--- # Pushwoosh API overview **URL:** https://docs.pushwoosh.com/developer/api-reference/ Learn how to authenticate, send requests, and handle responses when working with the Pushwoosh API. --- The Pushwoosh API allows developers to interact with the Pushwoosh platform. ## What can you do with Pushwoosh API? With Pushwoosh API you gain access to an extensive library of over 100 methods enabling you to: * Send personalized push notifications * Create, import, export and manage user segments for precise targeting * Utilize bulk APIs to manage users/devices in batches * Configure applications * Track user events and add tags to users to enhance targeting and personalization * Create and manage Pushwoosh Entities such as presets, tags, events, applications, etc. * Start Pushwoosh Customer Journeys via API * Retrieve comprehensive statistics on Customer Journeys and Push/Email/SMS Campaigns. * Implement geofencing to send location-based notifications when users enter or exit specific geographic areas * Seamlessly integrate 3rd party services with Pushwoosh To explore the full range of available methods and discover the extensive capabilities of the Pushwoosh API, please refer to the [API methods reference index](https://docs.pushwoosh.com/developer/api-reference/api-methods-reference-index). ## Authentication The Pushwoosh API uses API tokens to authenticate requests. There are two types of tokens: [**Server**](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) and [**Device**](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token), each used for specific API scopes. Each Pushwoosh account comes with a default API Access Token created for the Pushwoosh Demo App. You can view, create, and manage your API tokens in the Pushwoosh Control Panel. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) ## Rate limits Pushwoosh does not limit the number of API requests per account. The pace of delivery for [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) and the equivalent [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) requests is controlled by the `send_rate` parameter, whose allowed range comes from your account restrictions — by default 100 to 1000 messages per second. ## Requests HTTPS is mandatory for all API requests ensuring secure data transfer. ## Responses Pushwoosh API typically returns JSON responses. ### Success and error codes #### Success codes When your API request is processed successfully, you will receive a success code as part of the response. | HTTP status code | Status message | Description | | ---------------- | -------------- | -------------------------------------------- | | 200 | OK | Pushwoosh successfully processed the request | Please note that when you get an HTTP 200 response, it means that your request was understood and processed successfully, but it doesn’t guarantee the action’s success. So make sure to check for error or warning messages (access denied, application not found, invalid API key, etc) to determine the action’s success. | Status code | Status message | | ----------- | --------------------------------------------------------------------------------------------------- | | 200 | Warnings: You have reached the subscriber limit. Push Message will be sent to 1 random device only | | 200 | Warnings: You have exceeded the number of active devices allowed on your current subscription plan. | | 210 | Access denied or application not found | | 210 | Application not found | | 210 | Application suspended | | 210 | Account not found | | 210 | Account has been suspended | | 210 | Suspended account can not use device filtration | | 210 | API token or application is not set | | 210 | Please select at least one platform | | 210 | Invalid API Token: Account not found | | 210 | No application or group code given | | 210 | API token or application or account is not set | #### Error codes In case your request isn’t correctly formatted, encounters processing issues, or experiences any other errors, Pushwoosh will send you an error code indicating the nature of the issue, helping you troubleshoot and address the problem effectively. | HTTP status code | Status message | Description | | ---------------- | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | Bad request | The request can not be fulfilled due to bad syntax | | 401 | Unauthorized | Authentication has failed or hasn’t been provided | | 403 | Token restrictions forbid this operation. | Your access token lacks the necessary permissions to perform the requested action. To fix this, review your access token’s permissions in the Pushwoosh control panel and adjust them as needed to match the requirements for the specific operation you want to carry out. | | 404 | Resource not found | Indicates that the requested resource, such as an app, could not be located or does not exist on the server. | | 500 | Internal error | There was an unexpected issue or error on the server while processing the API request. | --- # API Access token **URL:** https://docs.pushwoosh.com/developer/api-reference/api-access-token/ Create and use API Access tokens in Pushwoosh to authenticate, manage projects, send messages, and access API features. --- API Access Token is a unique identifier assigned to a particular project or several projects in your account. It authenticates your account for managing your projects via the API, including account modifications, sending messages, editing projects, etc. ## API Access token types Pushwoosh provides two different API Access Token types: **Server** and **Device**. Each token type is only valid for its specific API scope. An authentication error will occur if the incorrect token is used. #### Server API token The Server API token is used with all API methods, except those under the Device API. It should be included in the request body in the `auth` field. #### Device API token The Device API token is used exclusively with Device API methods. It should be included in the HTTP request headers using the `Authorization` header. Below is an example of the correct header format: ```plaintext Authorization: Token ``` Replace `` with your actual Device API Token value. Methods requiring a Device API token The following API methods **require a Device API token** for authorization: * [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) * [`unregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#unregisterdevice) * [`deleteDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#deletedevice) * [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) * [`getTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#gettags) * [`setBadge`](https://docs.pushwoosh.com/developer/api-reference/device-api/#setbadge) * [`applicationOpen`](https://docs.pushwoosh.com/developer/api-reference/device-api/#applicationopen) * [`pushStat`](https://docs.pushwoosh.com/developer/api-reference/device-api/#pushstat) * [`messageDeliveryEvent`](https://docs.pushwoosh.com/developer/api-reference/device-api/#messagedeliveryevent) * [`registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#registeruser) * [`deleteUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#deleteuser) * [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) * [`registerEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api/#registeremail) * [`deleteEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api/#deleteemail) * [`setEmailTags`](https://docs.pushwoosh.com/developer/api-reference/email-api/#setemailtags) * [`registerEmailUser`](https://docs.pushwoosh.com/developer/api-reference/email-api/#registeremailuser) * [`getInboxMessages`](https://docs.pushwoosh.com/developer/api-reference/message-inbox/#getinboxmessages) * [`inboxStatus`](https://docs.pushwoosh.com/developer/api-reference/message-inbox/#inboxstatus) ## Create and manage API Access tokens You can view, create, and manage your API Access tokens in the **API Access** section of the Pushwoosh Control Panel. To access it, go to **Settings → API Access**. Your role needs both the **API access** and [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#available-permissions) permissions, because a token can read the whole audience. ![Pushwoosh Demo App token](https://docs.pushwoosh.com/api-reference-api-access-token-1.webp) By default, each account includes a token called **Pushwoosh Demo App**, which is set up as a server token. ### Create a new API Access token To create a new API Access token: 1. Go to the **API Access** section in the Control Panel. 2. Click **Generate New Token**. 3. In the window that appears, choose the token type: * [**Server**](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) used for most API methods (excluding Device API methods). * [**Device**](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) used for Device API methods only. 4. Enter a name for the token. 5. Click **Generate** to create the token. ![Generate a New Token](https://docs.pushwoosh.com/api-reference-api-access-token-4.webp) > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) The new token will appear in the API Access list. From there, you can copy its value for use in API requests or manage its access permissions as needed. ### Manage a token To manage an existing API token, click the **three-dot menu (⋮)** next to the token entry in the **API Access** section. ![Manage a token](https://docs.pushwoosh.com/api-reference-api-access-token-2.webp) The following options are available: #### Grant access to all projects Gives the token access to all projects in your account. This allows it to fully use the API across all projects. Tip Only give full access to tokens that need it. This helps keep your data secure and access controlled. #### Revoke access from all projects Removes the token’s access to all projects in your account. The token will no longer be able to use the API until access is given again. #### Edit token Opens the **Edit API Token Permissions** screen, where you can: * Change the token name * Copy the token value * Choose which projects the token can access by selecting or unselecting them in the list * Turn the token’s permissions on or off: Account modification, Applications modification, Tags modification, Message sending & deleting * Set the [allowed IP addresses](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#restrict-a-token-to-specific-ip-addresses) the token can be used from (Server tokens only) The **Permissions** tab also shows the token’s last used time and how many of its calls were refused in the last 7 and 30 days. ![Edit token screen with Permissions and Usage journal tabs, usage summary, and Allowed IP addresses field](https://docs.pushwoosh.com/api-reference-api-access-token-3.webp) After you finish making changes, click **Save** to apply them. Tip Only give tokens access to the projects they need. This helps protect your account and control what each token can do. #### Check a token’s usage journal Open the **Usage journal** tab on the Edit API Token Permissions screen to review every API call made with the token over the last 90 days. Choose an outcome (Any outcome, Accepted, Completed, Refused: IP address, Refused: no rights) and a date range, then click **Apply** to filter the list. Click **Export CSV** to download the filtered log as a CSV file. ![Usage journal tab with outcome and date filters and a CSV export button](https://docs.pushwoosh.com/api-reference-api-access-token-5.webp) #### Delete token Permanently removes the token from your account. Caution This action cannot be undone. Once deleted, the token can’t be recovered. Make sure the token is no longer in use before deleting it. ## Authorizing API requests To authorize API requests, use the correct type of API Access token based on the type of API method you are calling: #### For Server API methods Include the token in the “auth” field of the request body: Example: ```json { "request": { "application": "XXXXX-XXXXX", "auth": "yxoPUlwqm…………pIyEX4H", // paste your server token here "notifications": [{ "send_date": "now", "content": "Hello world" }] } } ``` #### For Device API methods Use the **Device token**. Include it in the HTTP request header using the following format: ```plaintext Authorization: Token ``` Replace `` with your actual token value. Caution Make sure the token type matches the API method you are using. If the token does not match the required type, the request will return an authentication error. ## Restrict a token to specific IP addresses A Server token can be restricted to a list of allowed source IP addresses. When the list is set, Pushwoosh only accepts API requests made with that token from a matching address. Device tokens don’t have this option. Open [**Edit token**](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) for a Server token and enter the addresses in **Allowed IP addresses**: * Add up to 50 entries, IPv4 or IPv6, each a single address (`203.0.113.7`) or a CIDR range (`203.0.113.0/24`). * A single address is stored as a full-length range, e.g. `203.0.113.7` becomes `203.0.113.7/32`. * A range is canonicalized to its network address, e.g. `203.0.113.7/24` becomes `203.0.113.0/24`. * `0.0.0.0/0` and `::/0` are rejected, since they would match every address and provide no restriction. * Leave the list empty to keep the token usable from any address. This is the default for existing and new tokens. Click **Save** to apply the list. Saving is rejected if the list has more than 50 entries or an entry that isn’t a valid address or CIDR range. ### What happens when a request comes from a disallowed address Pushwoosh checks the allowed IP addresses on every API call made with the token, whether it’s a v1 REST call (as documented across this API reference, for example the [Messages API](https://docs.pushwoosh.com/developer/api-reference/messages-api/)), a [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) call, or a [Customer Journey API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/) call. A request from an address outside the token’s allowed list is rejected. The response format differs by which calls you make: * Sending, canceling, and deleting messages, such as [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage), the channel-specific `create*Message` methods, [`cancelMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#cancelmessage), and [`deleteMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#deletemessage), return HTTP 200 with `"status_code": 403` in the response body, the same as their other authorization failures. * Other v1 REST calls, such as the Applications, Tags, Geozones, Segmentation (Filters), Campaigns, Presets, Test devices, and Statistics APIs, return a real HTTP 403 with a JSON error body. * **Messaging API v2** and the **Customer Journey API** return HTTP 403 with a `PermissionDenied` error. Allowlist changes take up to 10 minutes to apply everywhere Pushwoosh caches token validation for 10 minutes, and cache invalidation runs per data center. Narrowing or removing allowed addresses takes effect immediately in the data center where the change was made, and within 10 minutes in the others. --- # Key API identifiers **URL:** https://docs.pushwoosh.com/developer/api-reference/api-identifiers/ Reference guide to all Pushwoosh API identifiers, including app codes, user IDs, tokens, tags, segments, events, and more. --- This post lists essential identifiers used in Pushwoosh API requests. These identifiers are required for authentication, message creation, targeting, segmentation, tracking, and managing users across your applications. ## General identifiers ### Application code A unique identifier assigned to each application in your Pushwoosh account. It is required in most API calls to specify the target app. **Where to find it** Pushwoosh Control Panel → Below the application name. ### API access token A unique token assigned to a Pushwoosh account, used to authenticate API requests. It enables access to manage projects, send messages, modify settings, and retrieve data. Each account has at least one token created by default. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) **Where to find it** Pushwoosh Control Panel → API Access. ## Device identifiers ### Hardware ID A unique identifier assigned to a device by the Pushwoosh SDK. Automatically set by the SDK or passed during device registration. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid) **Where to find it** * In device logs. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#how-to-obtain-hwid-and-push-token) * In [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) when searching by **User ID** * In the CSV file downloaded from the [Segments list](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/segments/) ### Push token A token issued by platform providers that identifies an app-device pair for delivering push notifications. Collected automatically by the SDK when push permissions are granted. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#push-token) **Where to find it** * In device logs. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#how-to-obtain-hwid-and-push-token) * In [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/), enter the HWID in the search field to find the associated device, then open the device details to view the push token. ### Device type Indicates the platform of the target device using a numeric code. Set automatically by the SDK. Supported values: ```plaintext 1 – iOS 3 – Android 7 – macOS 8 – Windows 9 – Amazon 10 – Safari 11 – Chrome 12 – Firefox 13 – Internet Explorer 14 – Email 17 – Huawei 18 – SMS 20 – Web 21 – WhatsApp 22 – Line 23 – Kakao Talk 24 – Telegram 25 – Apple Wallet 26 – Google Wallet 27 – Viber ``` ## User identifiers ### User ID A custom identifier assigned to a user, enabling cross-device and personalized targeting. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/users-userids/) **Where to find it** Pushwoosh Control Panel → Audience → User Explorer ## Messaging identifiers ### Message code A unique identifier assigned to each message upon creation. It follows a specific format: **XXXX-XXXXXXXX-XXXXXXXX**. This code is essential for: * Tracking the status and delivery of a specific message * Canceling (or deleting) a previously sent message * Getting message statistics **Where to find it** * **Pushwoosh Control Panel:** Navigate to **Campaigns → One-time messages**, then open the **properties** of the desired message. The Message Code will be displayed there. * **[`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) API response:** When creating a new message via the API, the Message Code is included in the response. ### Request ID Correlates a message with the HTTP request that created it, letting you trace a single send end-to-end — from the HTTP call through Pushwoosh’s routing layer to the resulting message. Present on any message created by an HTTP call to [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) or [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/), including messages sent from the Control Panel. Messages created by Journeys, cron sends, or other non-HTTP internal triggers have no Request ID. **Setting your own value** Pass an `X-Request-ID` header with the request. If the value is 8–128 characters long and contains only Latin letters, digits, dots, underscores, and hyphens, Pushwoosh reuses it; otherwise — or if the header is omitted — Pushwoosh generates one for you. An invalid value is replaced silently, so the request never fails because of it. Uniqueness isn’t enforced, so use a value that’s actually unique per request if you rely on it for tracing. **Where to find it** Pushwoosh Control Panel → open a message → **Message Details** → **Show all properties** → **Message settings** → **Request ID** (with a copy button). **Example**: `a1b2c3d4e5f64a7b8c9d0e1f2a3b4c5d` Note This is unrelated to the `request_id` field returned by asynchronous/bulk endpoints — [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests/) and the [Audience API](https://docs.pushwoosh.com/developer/api-reference/audience-api/) `bulk*` methods. That `request_id` is a job token you poll for a result; it has nothing to do with HTTP-level tracing. ### In-app code A unique code that identifies an [instant in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) campaign. **Where to find it** Pushwoosh Control Panel → **Campaigns** → **Instant in-apps**. In the list, the code is displayed below the campaign name. **Example**: `AAAAA-BBBBB` ## Content identifiers ### Preset code An identifier for a saved message preset (for example, push or SMS). [Learn more](https://docs.pushwoosh.com/product/content/) **Where to find it** Pushwoosh Control Panel → Content → Presets. You can find the preset code in two places: * In the list view, displayed below the preset name. * In the URL when editing or creating a preset (it’s the last part of the URL). **Example**: URL: `https://app.pushwoosh.com/applications/XXXXX-XXXXX/sms-presets/edit/AAAAA-BBBBB` Preset code: `AAAAA-BBBBB` ### Rich media code Identifier for an in-app message template. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/) **Where to find it** Pushwoosh Control Panel → Content → Rich media. **Example:** URL: `https://app.pushwoosh.com/applications/XXXXX-XXXXX/rich-media/AAAAA-BBBBB/statistics` Rich media code: `AAAAA-BBBBB` ### Email content code The email content code is a unique identifier for saved email content. [Learn more](https://docs.pushwoosh.com/product/content/email-content/) **Where to find it** Pushwoosh Control Panel → Content → Email content. The code is the last part of the URL. **Example**: URL: `https://app.pushwoosh.com/applications/XXXXX-XXXXX/email-content/edit/AAAAA-BBBBB` Email content code: `AAAAA-BBBBB` ## Campaign identifiers ### Campaign code A unique identifier used to reference a specific campaign for tracking, and analysis. **Where to find it** The campaign code appears in the URL when viewing campaign-related statistics in the Pushwoosh Control Panel. To locate it, go to **Statistics → Aggregated statistics**, click on the desired campaign, and copy the code found at the end of the URL. **Example:** **URL:** `https://app.pushwoosh.com/applications/XXXXX-XXXXX/statistics/aggregated-message?campaignCode=YYYYY-YYYYY` **Campaign code:** `YYYYY-YYYYY` ### Journey ID A unique identifier used to associate messages with specific marketing campaigns for tracking and performance analysis. **Where to find it** * In the URL of the campaign canvas in the Pushwoosh Control Panel. * Select a point on the canvas and press **Ctrl+I** (Cmd+I) — copies `/` to the clipboard; the Journey ID is the part before the slash. **Example:** URL: `https://app.pushwoosh.com/journeys/XXXXX-XXXXX/1/aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee` Journey ID: `aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee` ## Segmentation and targeting identifiers ### Segment / Filter name The name of a predefined user segment created in Pushwoosh for audience targeting. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/segments/) **Where to find it** Pushwoosh Control Panel → Segments. ### Segment / Filter code A unique code in the format XXXXX-XXXXX that identifies a segment (also called a filter) in Pushwoosh. This code is used in API calls, automation logic, and dynamic segmentation rules. **Where to find it** * Via the [`/listFilters`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#listfilters) API method. * In the address bar when viewing a segment in the Pushwoosh Control Panel: **Example:** **URL:** `https://app.pushwoosh.com/applications/XXXXX-XXXXX/segments/XXXE9-XXX49/detail` **Segment code:** `XXXE9-XXX49` ### Tag A custom attribute assigned to users or devices. Used for dynamic segmentation and personalization. [Learn more](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/) **Where to find it** Pushwoosh Control Panel → Tags or via [`/getTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#gettags) API. ## Event identifiers ### Event name The name of a tracked event (e.g., `purchase`, `login`, `subscription`). Used to trigger automated workflows or for analytics. [Learn more](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/) **Where to find it** Pushwoosh Control Panel → Audience → Events. ## Geolocation identifiers ### Geozone ID Identifies a geofenced area used to trigger location-based notifications. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/) **Where to find it** Pushwoosh Control Panel → Geozones. --- # API methods reference index **URL:** https://docs.pushwoosh.com/developer/api-reference/api-methods-reference-index/ Find the right API method for your needs from over 100 available methods organized by functionality and use case. --- Pushwoosh provides a vast library of over 100 API methods, categorized for various functionalities like sending notifications, managing users, and tracking analytics. ## Messaging API v2 Unified API that replaces the entire `/create*Message` family (push, email, SMS, Kakao). See the [overview](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) and the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/). | | | | ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) | Creates and sends a single message through any channel. Replaces `/createMessage`, `/createTargetedMessage`, `/createEmailMessage`, `/createSMSMessage`, `/createKakaoMessage`. | | [`Cancel`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) | Cancels a previously created message before it is delivered. Use with `message_code` from [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). | | [`Update`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/) | Replaces a still-scheduled message with a new definition (full replace). Use with `message_code` from [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). | ## SMTP gateway Submit transactional email over standard SMTP instead of calling the API directly. The gateway authenticates with your application code and API token, then forwards each message to [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). See the [SMTP gateway reference](https://docs.pushwoosh.com/developer/api-reference/smtp-gateway/). ## Messages API | | | | ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api#createmessage) Deprecated | Creates a new push notification. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) instead. | | [`deleteMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api#deletemessage) | Deletes a message (sent or scheduled) and removes it from Message History. | | [`getMessageDetails`](https://docs.pushwoosh.com/developer/api-reference/messages-api#getmessagedetails) | Retrieves the message details. | | [`createTargetedMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api#createtargetedmessage) Deprecated | Creates a new targeted push notification. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `transactional` kind instead. | | [`getPushHistory`](https://docs.pushwoosh.com/developer/api-reference/messages-api#getpushhistory) Deprecated | Use [`/messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) to retrieve message history with detailed data. | | [`cancelMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api#cancelmessage) | Cancels a scheduled message, keeping it in Message History. | ## Presets API | | | | ---------------------------------------------------------------------- | ---------------------------------------------------------- | | [`Create`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#create) | Creates a new push preset. | | [`List`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#list) | Lists an application’s push presets. | | [`Get`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#get) | Retrieves a single push preset by code. | | [`Update`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#update) | Overwrites a push preset by code. | | [`UpdatePartial`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#updatepartial) | Updates only the supplied fields of a push preset by code. | | [`Clone`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#clone) | Duplicates a push preset within the same application. | | [`Delete`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#delete) | Deletes a push preset by code. | ## Device API | | | | ---------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | | [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) | Registers a device for the application. | | [`unregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#unregisterdevice) | Removes the device’s push token. | | [`deleteDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#deletedevice) | Deletes a device and all its associated data in the specified application. | | [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) | Sets tag values for the device. | | [`getTags`](https://docs.pushwoosh.com/developer/api-reference/device-api#gettags) | Retrieves tags with their corresponding values for a specific device. | | [`setBadge`](https://docs.pushwoosh.com/developer/api-reference/device-api#setbadge) | Sends the current badge value for a device to Pushwoosh. | | [`applicationOpen`](https://docs.pushwoosh.com/developer/api-reference/device-api#applicationopen) | Registers an app open event. | | [`pushStat`](https://docs.pushwoosh.com/developer/api-reference/device-api#pushstat) | Registers a push open event. | | [`messageDeliveryEvent`](https://docs.pushwoosh.com/developer/api-reference/device-api#messagedeliveryevent) | Registers a push delivery event for the device. | ## Audience API | | | | --------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | | [`bulkSetTags`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags) | Sets tag values for multiple devices. | | [`bulkSetTags Status`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags-status) | Returns the status of bulkSetTags operation. | | [`bulkRegisterDevice`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkregisterdevice) | Registers multiple devices in a single request, including optional tag values for each device. | | [`bulkRegisterDevice Status`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkregisterdevice-status) | Retrieves the status and results of a bulk device registration request using a request ID. | | [`bulkUnregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkunregisterdevice) | Unregisters multiple devices in a single request. | | [`bulkUnregisterDevice Status`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkunregisterdevice-status) | Retrieves the status and results of a bulk device unregistration request using a request ID. | ## Tags API | | | | ------------------------------------------------------ | ---------------------------------------------- | | [`addTag`](https://docs.pushwoosh.com/developer/api-reference/tags#addtag) | Creates a tag in your account. | | [`deleteTag`](https://docs.pushwoosh.com/developer/api-reference/tags#deletetag) | Removes a tag with all associated information. | | [`listTags`](https://docs.pushwoosh.com/developer/api-reference/tags#listtags) | Retrieves a list of tags on the account. | ## Segmentation (Filters) API | | | | -------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | [`createFilter`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#createfilter) | Creates a new segment. | | [`listFilters`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#listfilters) | Returns a list of available segments with their conditions. | | [`deleteFilter`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#deletefilter) | Deletes an existing segment. | | [`exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#exportsegment) | Schedules a request to export subscribers matching segment conditions. | | [`exportSegment Results`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#exportsegment-results) | Retrieves the CSV link for exportSegment results. | ## User-centric API | | | | ------------------------------------------------------------------------ | --------------------------------------------------------------------- | | [`registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser) | Associates external User ID with a specified device. | | [`deleteUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#deleteuser) | Deletes a user and all devices associated with the specified User ID. | | [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) | Calls the event within the app. | ## Events API | | | | ------------------------------------------------------------ | ----------------------------- | | [`createEvent`](https://docs.pushwoosh.com/developer/api-reference/events#createevent) | Creates an event for the app. | ## Applications API | | | | ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------- | | [`createApplication`](https://docs.pushwoosh.com/developer/api-reference/applications#createapplication) | Creates a new application on the account. | | [`updateApplication`](https://docs.pushwoosh.com/developer/api-reference/applications#updateapplication) | Updates the application settings. | | [`deleteApplication`](https://docs.pushwoosh.com/developer/api-reference/applications#deleteapplication) | Deletes the app from the account. | | [`getApplication`](https://docs.pushwoosh.com/developer/api-reference/applications#getapplication) | Gets details about the app. | | [`getApplications`](https://docs.pushwoosh.com/developer/api-reference/applications#getapplications) | Retrieves a list of applications on the account. | | [`getApplicationFile`](https://docs.pushwoosh.com/developer/api-reference/applications#getapplicationfile) | Retrieves the configuration files related to the app. | | [`setApplicationPlatformStatus`](https://docs.pushwoosh.com/developer/api-reference/applications#setapplicationplatformstatus) | Changes the platform status of the app. | ## Control Groups API | | | | --------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | [`List`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#list) | Lists an application’s control groups. | | [`Create`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#create) | Creates a control group. | | [`Get`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#get) | Retrieves a single control group by code. | | [`UpdatePercentage`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatepercentage) | Resizes a control group’s hold-out percentage. | | [`UpdateCountries`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatecountries) | Rescopes a control group to a set of countries. | | [`Rename`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#rename) | Changes the name the Control Panel shows for a control group. | | [`Disable`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#disable) | Switches a control group off. | | [`Reshuffle`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#reshuffle) | Redraws a control group’s hold-out sample. | | [`ForceUpdateCalculation`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#forceupdatecalculation) | Starts a fresh count of a control group’s size. | | [`GetCalculationStatus`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#getcalculationstatus) | Polls a running size calculation. | | [`GetAnalytics`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#getanalytics) | Retrieves Control-vs-Treatment analytics for a control group. | | [`CheckControlGroupMembership`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#checkcontrolgroupmembership) | Reports whether each of a batch of user IDs is held out by a control group. | | [`Delete`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#delete) | Removes a control group. | ## Data Erasure API | | | | --------------------------------------------------------------------------------- | --------------------------------------------------------------- | | [`Create`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#create) | Starts erasing (or dry-run counting) a subject’s personal data. | | [`Upload`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#upload) | Creates an erasure task from an uploaded CSV of identifiers. | | [`ListTasks`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#listtasks) | Lists the account’s erasure tasks. | | [`GetTask`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#gettask) | Retrieves one erasure task’s status and counters. | | [`ListTaskRows`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#listtaskrows) | Lists one task’s per-identifier results. | | [`GetTaskReport`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#gettaskreport) | Downloads one task’s report as CSV. | | [`GetJournalReport`](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#getjournalreport) | Downloads every task of a period as one CSV. | ## Campaigns API | | | | --------------------------------------------------------------------- | ------------------------------------------ | | [`createCampaign`](https://docs.pushwoosh.com/developer/api-reference/campaigns#createcampaign) | Creates a new push campaign. | | [`deleteCampaign`](https://docs.pushwoosh.com/developer/api-reference/campaigns#deletecampaign) | Deletes a particular campaign. | | [`getCampaigns`](https://docs.pushwoosh.com/developer/api-reference/campaigns#getcampaigns) | Retrieves a list of campaigns for the app. | ## App configuration API | | | | ------------------------------------------------------------------------------------------------------------------- | -------------------------------------------- | | [`configureApplication for iOS`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-ios) | Configures the iOS platform for the app. | | [`configureApplication for Android`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-android) | Configures the Android platform for the app. | | [`configureApplication for Mac OS X`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-mac-os-x) | Configures the macOS platform for the app. | | [`configureApplication for Windows`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-windows) | Configures the Windows platform for the app. | | [`configureApplication for Amazon`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-amazon) | Configures the Amazon platform for the app. | | [`configureApplication for Chrome`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-chrome) | Configures the Chrome platform for the app. | | [`configureApplication for Safari`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-safari) | Configures the Safari platform for the app. | | [`configureApplication for Firefox`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-firefox) | Configures the Firefox platform for the app. | | [`configureApplication for Email`](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-email) | Configures the Email platform for the app. | ## Email API | | | | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api#createemailmessage) Deprecated | Creates an email message. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["EMAIL"]` and an [`email_payload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) instead. | | [`registerEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api#registeremail) | Registers an email address for the app. | | [`deleteEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api#deleteemail) | Removes an email address from your database. | | [`setEmailTags`](https://docs.pushwoosh.com/developer/api-reference/email-api#setemailtags) | Sets tag values for the specified email address. | | [`registerEmailUser`](https://docs.pushwoosh.com/developer/api-reference/email-api#registeremailuser) | Associates an external User ID with a specified email address. | ## Email Templates API | | | | ---------------------------------------------------------------- | ----------------------------------------------------------- | | [`Create`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#create) | Creates a new email template in an application. | | [`List`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#list) | Lists an application’s email templates. | | [`Get`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#get) | Returns a single email template by code. | | [`Update`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#update) | Updates an existing email template by code. | | [`Delete`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#delete) | Deletes an email template and its connected preset by code. | | [`Clone`](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#clone) | Clones an email template into a destination application. | ## Test devices API | | | | ---------------------------------------------------------------------------- | ---------------------------------------------------------- | | [`createTestDevice`](https://docs.pushwoosh.com/developer/api-reference/test-devices#createtestdevice) | Registers a test device for the application. | | [`listTestDevices`](https://docs.pushwoosh.com/developer/api-reference/test-devices#listtestdevices) | Retrieves all test devices registered for the application. | ## Message inbox API | | | | ------------------------------------------------------------------------------ | ---------------------------------------------------------- | | [`getInboxMessages`](https://docs.pushwoosh.com/developer/api-reference/message-inbox/#getinboxmessages) | Retrieves the list of Inbox messages for a specified HWID. | | [`inboxStatus`](https://docs.pushwoosh.com/developer/api-reference/message-inbox#inboxstatus) | Updates the status of a specific Inbox message. | ## Statistics API ### Message statistics | | | | ---------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------- | | [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) | Retrieves a list of sent messages. | | [`totalsByIntervals`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#totalsbyintervals) | Provides metrics and conversion data aggregated by hour. | | [`getDeliveryFunnel`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getdeliveryfunnel) | Returns one message’s delivery funnel, broken down by channel and platform. | | [`getCampaignDeliveryFunnel`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getcampaigndeliveryfunnel) | Sums the delivery funnel over a campaign’s messages. | | [`getMessageLog`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog) | Retrieves detailed information about sent messages. | | [`linksInteractions`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#emailslinksinteractions) | Provides statistics on email link clicks. | | [`linksInteractionsDevices`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#emailslinksinteractionsdevices) | Lists users who clicked on links in emails. | | [`bouncedEmails`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#bouncedemails) | Provides information on email bounces, complaints, and bounce reasons. | ### Asynchronous message statistics export | | | | ------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | [`exportMessagesStatistics/export`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#export) | Queues a message history export to a CSV file, without the row limits of `messages:list`. | | [`exportMessagesStatistics/status`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#status) | Returns the progress of an export task. | | [`exportMessagesStatistics/result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) | Returns the generated file name once the task has completed. | | [`exportMessagesStatistics/lastTasks`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#lasttasks) | Lists recent export tasks for an application. | | [`exportMessagesStatistics/delete`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#delete) | Deletes a task and its file before the retention window expires. | | [`exportMessagesStatistics/download`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#download) | Downloads the generated CSV file by name. | ### Application and subscriber statistics | | | | --------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | [`getApplicationSubscribersStats`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/#getapplicationsubscribersstats) | Retrieves the app’s subscriber statistics, grouped by device type. | | [`getAppStats`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/#getappstats) | Retrieves app statistics for a specific time period. | | [`getSubscribersStatistics`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/#getsubscribersstatistics) | Retrieves subscriber statistics for a specific time period. | ### Campaign statistics | | | | ---------------------------------------------------------------------------------- | --------------------------------------------------------- | | [`getCampaignStats`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/) | Retrieves campaign statistics for a specific time period. | ### Events and tags statistics | | | | -------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | [`getEventStatistics`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#geteventstatistics) | Retrieves the number of times an event was triggered during a specific period. | | [`getTagStats`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#gettagstats) | Retrieves statistics for a specific tag. | ### In-app statistics | | | | --------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | | [`inapps:totals`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/inapp-statistics/#inappstotals) | Retrieves impression, interaction and frequency capping totals for one or more in-app campaigns over a period. | | [`inapps:timeline`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/inapp-statistics/#inappstimeline) | Retrieves period totals and a time series of in-app metrics for a single campaign. | ## iOS Live Activities API | | | | ------------------------------------------------------------------------------------------- | -------------------------------------- | | [`startLiveActivity`](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api#startliveactivity) | Creates a new iOS Live Activity. | | [`updateLiveActivity`](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api#updateliveactivity) | Updates an existing iOS Live Activity. | ## Apple Wallet PassKit API | | | | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- | | [`Apple Wallet PassKit API`](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) | Reference for managing Apple Wallet passes and related configuration in Pushwoosh. | ## Google Wallet API | | | | ------------------------------------------------------------------------------------ | ---------------------------------------------------------------------- | | [`Create a pass`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#create-a-pass) | Creates the pass in Google Wallet and returns a save link. | | [`Validate a pass`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#validate-a-pass) | Checks a pass configuration without creating it. | | [`Update a pass`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#update-a-pass) | Updates an existing pass. Google delivers the change to saved devices. | | [`Get a save link`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#get-a-save-link) | Returns an “Add to Google Wallet” save link for an existing pass. | | [`Get a pass`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#get-a-pass) | Returns a single stored pass. | | [`List passes`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#list-passes) | Returns a paginated list of passes for an application. | | [`Set pass state`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#set-pass-state) | Activates or invalidates a pass. | | [`Delete a pass`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#delete-a-pass) | Invalidates the pass in Google and removes its Pushwoosh record. | | [`Get configuration`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#get-configuration) | Returns the Google Wallet configuration state for an application. | | [`Templates`](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#templates) | Lists pass templates or fetches one as a starting point. | ## Customer Journey API | | | | ----------------------------------------------------------------------------------------- | -------------------------------------------------------------------- | | [`Lifecycle`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) | Starts, pauses, finishes, drafts, or archives a journey by its UUID. | | [`Create and update`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) | Creates a new journey definition or replaces an existing one. | | [`Start by API`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) | Injects users into the API entry point of a running journey. | | [`Get Journey stats`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) | Obtains stats for a specific journey by its ID. | | [`Remove users from journeys`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/) | Removes one or more users from all or selected active journeys. | ## Scheduled requests API | | | | ---------------------------------------------------------------------- | -------------------------------------------- | | [`getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) | Retrieves the result of a scheduled request. | ## SMS API | | | | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------- | | [`createSMSMessage`](https://docs.pushwoosh.com/developer/api-reference/sms#createsmsmessage) Deprecated | Creates a new SMS message. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["SMS"]` instead. | ## WhatsApp API | | | | --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | [`createWhatsAppMessage`](https://docs.pushwoosh.com/developer/api-reference/whatsapp#createwhatsappmessage) Deprecated | Creates a new WhatsApp message. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["WHATS_APP"]` instead. | ## LINE API | | | | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | | [`createLineMessage`](https://docs.pushwoosh.com/developer/api-reference/line-api/) Deprecated | Creates a new LINE message. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["LINE"]` instead. | ## Kakao API | | | | ------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | | [`createKakaoMessage`](https://docs.pushwoosh.com/developer/api-reference/kakao/#createkakaomessage) Deprecated | Sends a Kakao message using an approved template. Use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["KAKAO"]` instead. | ## Geozones API | | | | -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | [`getNearestZone`](https://docs.pushwoosh.com/developer/api-reference/geozones#getnearestzone) | Retrieves the parameters of the nearest geozone and the distance to it. Also records the device location for geo push notifications. | | [`addGeoZone`](https://docs.pushwoosh.com/developer/api-reference/geozones#addgeozone) | Adds a Geozone to a specific app. | | [`updateGeoZone`](https://docs.pushwoosh.com/developer/api-reference/geozones#updategeozone) | Updates Geozone properties such as name, status, content, and range. | | [`deleteGeoZone`](https://docs.pushwoosh.com/developer/api-reference/geozones#deletegeozone) | Removes Geozones from the app. | | [`addGeoZoneCluster`](https://docs.pushwoosh.com/developer/api-reference/geozones#addgeozonecluster) | Adds a Geozone Cluster to the app. | | [`deleteGeoZoneCluster`](https://docs.pushwoosh.com/developer/api-reference/geozones#deletegeozonecluster) | Removes a Geozone Cluster from the app. | | [`listGeoZones`](https://docs.pushwoosh.com/developer/api-reference/geozones#listgeozones) | Retrieves a list of Geozones for the app. | | [`listGeoZoneClusters`](https://docs.pushwoosh.com/developer/api-reference/geozones#listgeozoneclusters) | Retrieves a list of Geozone clusters for the app. | --- # App configuration API **URL:** https://docs.pushwoosh.com/developer/api-reference/app-configuration/ Configure app platforms using API methods for iOS, Android, macOS, Amazon, Chrome, Safari, and Firefox push notification services. --- The App configuration API sets up the platforms of an existing Pushwoosh application, such as the iOS key file or the Android FCM service account. All platforms use the same `/configureApplication` method, and the sections below list the request parameters for each platform. To create the application itself, use the [Applications API](https://docs.pushwoosh.com/developer/api-reference/applications/). ## configureApplication for iOS Configures iOS platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | -------------------------- | ------- | ----------------------------------------------------------------------------------------------------------- | | auth | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel | | application | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type | integer | Must be 1 for iOS | | ios_key_management_type | string | ”manual” | | ios_key_file | string | Base64-encoded iOS key file content | | ios_key_password | string | Password for iOS key file | | ios_gateway | integer | ”0” for sandbox, “1” for production | | ios_framework | string | Possible values: “native”, “cordova”, “titanium” | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": true, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 1, // required. 1 for iOS "ios_key_management_type": "manual", // required. "manual" "ios_key_file": "MIIMY...BAQ==", // required. base64-encoded iOS key file content "ios_key_password": "password", // required. "ios_gateway": 1, // required. "0" (sandbox) | "1" (production) "ios_framework": "native" // required. possible values: "native", "cordova", "titanium", // "marmalade", "adobeAir", "corona", "xamarin", // "phoneGapBuild", "triggerIO", "unity" } } ``` ## configureApplication for Android Configures Android platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | -------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from your Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 3 for Android. | | android_framework* | string | Android framework. Possible values: native, cordova, marmalade, adobeAir, corona, xamarin, phoneGapBuild, triggerIO, titanium, unity. | | android_fcm_service_account* | object | FCM service account key (JSON file generated in Firebase project settings). | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": true, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` - Example ```json { "request": { "auth": "Z2PLR....IRwTqHwCr", "application": "87E33-XXXXX", "device_type": 3, "android_fcm_service_account": { "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "client_email": "****@foobar.iam.gserviceaccount.com", "client_id": "115********6584854983", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-zffgu%40foobar.iam.gserviceaccount.com", "private_key": "-----BEGIN PRIVATE KEY-----\nuH/OU/fIL+i6Wm8as...\n-----END PRIVATE KEY-----\n", "private_key_id": "e3749********6d9", "project_id": "app-android", "token_uri": "https://oauth2.googleapis.com/token", "type": "service_account", "universe_domain": "googleapis.com" }, "android_framework": "native" } } ``` ## configureApplication for Mac OS X Configures Mac OS X platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | ------------------------------ | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 7 for Mac OS X. | | macos_key_management_type* | string | Only “manual” type is available currently. | | macos_cert_file | string | Base64-encoded Mac OS X certificate file content. | | macos_key_file | string | Base64-encoded Mac OS X key file content. | | macos_key_password | string | Key file password. | | macos_gateway* | integer | Push gateway. “0” for sandbox, “1” for production. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": true, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 7, // required. 7 for Mac OS X "macos_key_management_type": "manual", // required. "manual" "macos_cert_file": "iVBORw0KGgoAAAANSUhEUgAABB…", // optional. Base64-encoded content of Mac OS X certificate file "macos_key_file": "iVBORw0KGgoAAAANSUhEUgAABB…", // optional. Base64-encoded content of Mac OS X key file "macos_key_password": "PASSWORD", // optional. Key file password "macos_gateway": 1 // required. push gateway. "0" for sandbox or "1" for production } } ``` ## configureApplication for Windows Configures Windows platforms for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 8 for Windows. | | wns_psid* | string | Package security identifier from Windows Store for the app. Starts with “ms-app://“. | | wns_secret* | string | Client Secret from Windows Store for the app. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": true } } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 8, // required. 8 for Windows "wns_psid": "ms-app://…", // required. package security identifier of the app from Windows Store "wns_secret": "CLIENT_SECRET" // required. Windows client secret } } ``` ## configureApplication for Amazon Configures Amazon platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | ---------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 9 for Amazon. | | adm_cid* | string | Amazon client ID. | | adm_secret* | string | Amazon client secret. | | adm_framework* | string | Amazon framework. Possible values: *native, unity, adobeAir.* | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": true, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 9, // required. 9 for Amazon "adm_cid": "CLIENT_ID", // required. Amazon client ID "adm_secret": "CLIENT_SECRET", // required. Amazon Client Secret "adm_framework": "native" // required. possible values: native, unity, adobeAir. } } ``` ## configureApplication for Chrome Configures Chrome platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | Required | | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | -------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | Required | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | Required | | device_type* | integer | Must be 11 for Chrome. | Required | | chrome_fcm_service_account* | object | FCM service account key (JSON file generated in Firebase project settings). | Required | | chrome_vapid_key* | string | Public VAPID (Voluntary Application Server Identification) key used for push notification authentication in Chrome. | Required | | chrome_vapid_private_key* | string | Private VAPID key used for signing and sending push notifications in Chrome. | Required | ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": true, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` * Example ```json { "request": { "auth": "Z2PLR....IRwTqHwCr", "application": "87E33-XXXXX", "device_type": 11, "chrome_fcm_service_account": { "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "client_email": "****@foobar.iam.gserviceaccount.com", "client_id": "11529......6584854983", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/****foobar.iam.gserviceaccount.com", "private_key": "-----BEGIN PRIVATE KEY-----\nuH/OU/fIm8as...\n-----END PRIVATE KEY-----\n", "private_key_id": "e3749......e66d9", "project_id": "app-chrome", "token_uri": "https://oauth2.googleapis.com/token", "type": "service_account", "universe_domain": "googleapis.com" }, "chrome_vapid_key": "BEr....KPqQvCbU", "chrome_vapid_private_key": "ee....flOqmSo" } } ``` ## configureApplication for Safari Configures Safari platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 10 for Safari. | | safari_key_management_type* | string | Only “manual” type is available currently. | | safari_website_name | string | Website name. | | safari_allowed_domains | array | Ex: ”, ” | | safari_url_template* | string | Safari URL template formatted as “https://%@“. | | safari_icon_file | string | Base64-encoded content of 256x256px png file. | | safari_key_file* | string | Base64-encoded Safari .p12 key file content. | | safari_key_password | string | Password to the key file. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": true, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 10, // required. 10 for Safari "safari_key_management_type": "manual", // required. "manual" "safari_website_name": "WEBSITE_NAME", // optional. "safari_allowed_domains": [ // optional. "https://site1.com", "https://site2.com" ], "safari_url_template": "https://%@", // required. "safari_icon_file": "iVBORw0KGgoAAAANSUhEUgAABB…", // optional. Base64-encoded content of 256x256px png file "safari_key_file": "MIIMY...BAQ==", // required. base64-encoded Safari .p12 key file content "safari_key_password": "PASSWORD" // optional. Password to the key file above } } ``` ## configureApplication for Firefox Configures Firefox platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | Required | | -------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------- | -------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | Required | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | Required | | device_type* | integer | Must be 12 for Firefox. | Required | | firefox_is_enabled | boolean | Enables push notifications for Firefox. | Optional | | firefox_fcm_service_account* | object | JSON generated in Firebase project settings. | Required | | firefox_vapid_key* | string | Public VAPID (Voluntary Application Server Identification) key used for push notification authentication in Firefox. | Required | | firefox_vapid_private_key* | string | Private VAPID key used for signing and sending push notifications in Firefox. | Required | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": true, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` - Example ```json { "request": { "auth": "Z2PLR....IRwTqHwCr", "application": "87E33-XXXXX", "device_type": 12, "firefox_fcm_service_account": { "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs", "auth_uri": "https://accounts.google.com/o/oauth2/auth", "client_email": "****@foobar.iam.gserviceaccount.com", "client_id": "115........6584854983", "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/****@foobar.iam.gserviceaccount.com", "private_key": "-----BEGIN PRIVATE KEY-----\n****uH/OU/fILL****fMJZ+i6****um8as.....\n-----END PRIVATE KEY-----\n", "private_key_id": "e3........8e66d9", "project_id": "app-firefox", "token_uri": "https://oauth2.googleapis.com/token", "type": "service_account", "universe_domain": "googleapis.com" }, "firefox_vapid_key": "BEr....KPqQvCbU", "firefox_vapid_private_key": "ee....flOqmSo" } } ``` ## configureApplication for Email Configures Email platform for the app. `POST` `https://api.pushwoosh.com/json/1.3/configureApplication` #### Request Body | Name | Type | Description | | -------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | device_type* | integer | Must be 14 for Email. | | email_from_email* | string | Sender email address. Should be valid and verified. | | email_from_name* | string | Sender name. | | email_reply* | string | Email address to reply to. Should be valid and verified. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "providers": { "ios": false, "amazon": false, "android": false, "chrome": false, "email": true, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false } } } ``` Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "device_type": 14, // required. 14 for email "email_from_email": "email@domain.com", // required. Email address should be valid and verified "email_from_name": "SENDER_NAME", // required. Name to display in the "From" email field "email_reply": "email@domain.com" // required. Email address to reply should be valid and verified } } ``` --- # Apple Wallet PassKit API **URL:** https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/ Create, update, and manage Apple Wallet passes (.pkpass) programmatically through the Pushwoosh PassKit Designer API. --- The PassKit Designer API lets you create, update, download, and manage [Apple Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) programmatically. It supports the same operations the pass builder in the Control Panel performs. Use it to issue loyalty cards, coupons, event tickets, boarding passes, and store cards, and to push live updates to passes already installed on your users’ devices. Prerequisite: PassKit certificate Before you can create passes for an application, an iOS PassKit signing certificate must be configured for that application. The certificate is managed in the Pushwoosh Control Panel. Without it, `create` and `update` requests fail. See [Apple Wallet pass configuration for iOS](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/). ## Base URL ```plaintext https://apple-passkit.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise. ## Authentication Every request must include an `Authorization` header with your [Pushwoosh API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/): ```plaintext Authorization: Token ``` The account that owns the token must own the application referenced by `applicationCode`. A request for an application that belongs to another account returns `403 Forbidden`. ## Conventions * **Field naming:** JSON fields use `lowerCamelCase` (for example, `passTypeIdentifier`, `serialNumber`, `backgroundColor`). * **Unpopulated fields:** responses include all fields, even when empty or zero-valued. * **Binary data:** `bytes` fields such as `pkpassData` and image `data` are Base64-encoded strings in JSON. * **Serial numbers:** the `serialNumber` is always assigned by the server when a pass is created. Any value you send on create is ignored; it identifies the pass for all later operations. ### Error responses The API maps internal status codes to HTTP status codes: | HTTP status | Meaning | | :------------------------ | :--------------------------------------------------------- | | `400 Bad Request` | Invalid argument—a required field is missing or malformed. | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application does not belong to the caller’s account. | | `404 Not Found` | The pass, template, or application was not found. | | `503 Service Unavailable` | The service is at capacity or temporarily unavailable. | ## Endpoints | Method | Path | Description | | :------- | :---------------------------------------------------- | :------------------------------------------ | | `POST` | `/api/pass/validate` | Validate a pass configuration | | `POST` | `/api/pass/create` | Create a new `.pkpass` | | `POST` | `/api/pass/update/{serialNumber}` | Update an existing pass and notify devices | | `GET` | `/api/passes` | List all passes for an application | | `GET` | `/api/pass/{applicationCode}/{serialNumber}` | Get a single pass | | `GET` | `/api/pass/{applicationCode}/{serialNumber}/download` | Download the `.pkpass` of an existing pass | | `DELETE` | `/api/pass/{applicationCode}/{serialNumber}` | Delete a pass | | `GET` | `/api/pass/{serialNumber}/registrations` | List devices registered for a pass | | `GET` | `/api/config` | Get the application’s PassKit configuration | | `GET` | `/api/templates` | List available pass templates | | `GET` | `/api/templates/{filename}` | Get a single template | ## Create a pass Generates, signs, and stores a new pass, then returns its server-assigned serial number. `POST` `/api/pass/create` Tip To obtain the `.pkpass` file itself, call [Download a pass](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#download-a-pass) (or build an [install link / QR code](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#share-a-pass-as-a-qr-code)) with the returned serial. ### Request body | Parameter | Type | Required | Description | | :---------------- | :--------------- | :------- | :----------------------------------------------------------------------------------------------------- | | `pass` | object | Yes | The [pass object](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-object) describing the pass. | | `images` | array of objects | No | [Pass images](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-image-object) (icon, logo, etc.). `icon` and `logo` are required for a valid pass. | | `userId` | string | Yes | The Pushwoosh User ID the pass is issued to. | | `applicationCode` | string | Yes | The [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | Note On create, if `teamIdentifier`, `passTypeIdentifier`, or `organizationName` are omitted, they default to the values from the application’s configured certificate. `formatVersion` defaults to `1`. The `serialNumber` is assigned server-side. ##### Request example ```json { "applicationCode": "XXXXX-XXXXX", "userId": "user-123", "pass": { "description": "Acme loyalty card", "logoText": "Acme", "backgroundColor": "rgb(60, 65, 76)", "foregroundColor": "rgb(255, 255, 255)", "labelColor": "rgb(255, 255, 255)", "storeCard": { "primaryFields": [ { "key": "balance", "label": "BALANCE", "value": "1200 pts" } ], "secondaryFields": [ { "key": "member", "label": "MEMBER", "value": "Jane Doe" } ] }, "barcodes": [ { "format": "PKBarcodeFormatQR", "message": "1234567890", "messageEncoding": "iso-8859-1" } ] }, "images": [ { "imageType": "icon", "data": "", "contentType": "image/png" }, { "imageType": "logo", "data": "", "contentType": "image/png" } ] } ``` ### Response | Field | Type | Description | | :------------- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `serialNumber` | string | Server-assigned unique identity of the created pass. Use it to fetch the pass ([Get a pass](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#get-a-pass)) or download the `.pkpass` ([Download a pass](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#download-a-pass)). | | `message` | string | Result message. | ##### Response example ```json { "serialNumber": "a1b2c3d4-1234-5678-9abc-def012345678", "message": "Pass created successfully" } ``` ## Validate a pass Checks a pass configuration against Apple’s specifications without creating a file. Useful before calling create. `POST` `/api/pass/validate` ### Request body | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :------------------------------------------- | | `pass` | object | Yes | The [pass object](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-object) to validate. | ### Response | Field | Type | Description | | :--------- | :--------------- | :------------------------------------ | | `valid` | boolean | Whether the pass passes validation. | | `errors` | array of strings | Blocking problems that must be fixed. | | `warnings` | array of strings | Non-blocking advisories. | ## Update a pass Regenerates the pass with new content, re-signs it, increments its update tag, and sends a **silent push notification** to every device that registered the pass. iOS then fetches and installs the updated version in the background. `POST` `/api/pass/update/{serialNumber}` ### Path parameters | Parameter | Type | Description | | :------------- | :----- | :---------------------------------------------------- | | `serialNumber` | string | The serial number returned when the pass was created. | ### Request body | Parameter | Type | Required | Description | | :---------------- | :----- | :------- | :--------------------------------------------------------- | | `updates` | object | Yes | The full [pass object](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-object) with the new content. | | `applicationCode` | string | Yes | The Pushwoosh application code. | The `serialNumber` (from the path) and the pass’s authentication token are preserved by the server regardless of what you send. ### Response | Field | Type | Description | | :---------- | :------ | :--------------------------------- | | `success` | boolean | Whether the update succeeded. | | `updateTag` | integer | New update tag (a Unix timestamp). | | `message` | string | Result message. | ## List passes Returns a paginated, sorted list of the passes stored for an application. `GET` `/api/passes?applicationCode=XXXXX-XXXXX&page=0&perPage=20` ### Query parameters | Parameter | Type | Required | Description | | :---------------- | :------ | :------- | :------------------------------------------------------- | | `applicationCode` | string | Yes | The Pushwoosh application code. | | `orderBy` | string | No | Sort field: `UPDATED` (default) or `CREATED`. | | `orderDirection` | string | No | Sort direction: `DESC` (default, newest first) or `ASC`. | | `page` | integer | No | Zero-based page index. Defaults to `0`. | | `perPage` | integer | No | Page size. `0` or omitted uses the server default. | ### Response | Field | Type | Description | | :-------- | :--------------- | :----------------------------------------------------------- | | `passes` | array of objects | The current page of [pass records](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-record-object). | | `page` | integer | The returned page index. | | `perPage` | integer | The page size used for this response. | | `total` | integer | Total number of passes for the application across all pages. | ##### Response example ```json { "passes": [ /* pass records */ ], "page": 0, "perPage": 20, "total": 137 } ``` ## Get a pass Returns a single stored pass, including its full pass object. `GET` `/api/pass/{applicationCode}/{serialNumber}` ### Path parameters | Parameter | Type | Description | | :---------------- | :----- | :------------------------------ | | `applicationCode` | string | The Pushwoosh application code. | | `serialNumber` | string | The pass serial number. | ### Response Returns `{ "pass": { ... } }`, a single [pass record](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-record-object). ## Download a pass Returns the stored `.pkpass` file of an existing pass. `GET` `/api/pass/{applicationCode}/{serialNumber}/download` ### Response | Field | Type | Description | | :----------- | :-------------- | :------------------- | | `pkpassData` | string (Base64) | The `.pkpass` file. | | `filename` | string | Suggested file name. | ## Delete a pass Removes a pass record and its stored `.pkpass` file. `DELETE` `/api/pass/{applicationCode}/{serialNumber}` ### Response | Field | Type | Description | | :-------- | :------ | :---------------------------- | | `success` | boolean | Whether the pass was deleted. | | `message` | string | Result message. | ## Get pass registrations Lists the devices that have added the pass and are registered for updates. `GET` `/api/pass/{serialNumber}/registrations?applicationCode=XXXXX-XXXXX` ### Response Returns `{ "registrations": [ ... ] }`, where each item has: | Field | Type | Description | | :------------------------ | :----- | :---------------------------------- | | `deviceLibraryIdentifier` | string | Apple device library identifier. | | `pushToken` | string | The pass push token for the device. | ## Get configuration Returns the PassKit configuration resolved for an application from its certificate. `GET` `/api/config?applicationCode=XXXXX-XXXXX` ### Response | Field | Type | Description | | :------------------- | :------ | :------------------------------------------------------------------------------------------------------------------------------- | | `teamIdentifier` | string | Apple Team ID from the certificate. | | `passTypeIdentifier` | string | Pass Type ID from the certificate. | | `organizationName` | string | Organization name from the certificate. | | `hasCertificate` | boolean | Whether a certificate is configured. | | `webServiceUrl` | string | Base URL of the pass web service. Clients build an install link by appending `/v1/passes/{passType}/{serial}?token={authToken}`. | ## Templates List the available pass templates, or fetch one as a [pass object](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-object) you can use as a starting point. `GET` `/api/templates` — returns `{ "templates": [ { "filename", "name", "description", "style" } ] }`. `GET` `/api/templates/{filename}` — returns `{ "template": { ...pass object... } }`. ## Share a pass as a QR code To let users add a pass by scanning a QR code (or tapping a link), encode the pass **install URL** into a QR code. The URL is built from values you already get back from the API: ```plaintext {webServiceUrl}/v1/passes/{passTypeIdentifier}/{serialNumber}?token={authenticationToken} ``` | URL part | Where to get it | | :-------------------- | :----------------------------------------------------------------------------- | | `webServiceUrl` | [`GET /api/config`](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#get-configuration) → `webServiceUrl` | | `passTypeIdentifier` | [pass record](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-record-object) → `pass.passTypeIdentifier` (from list/get) | | `serialNumber` | [pass record](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-record-object) → `serialNumber` | | `authenticationToken` | [pass record](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-record-object) → `pass.authenticationToken` | Example: ```plaintext https://apple-passkit.svc-nue.pushwoosh.com/v1/passes/pass.com.acme.loyalty/a1b2c3d4-1234-5678-9abc-def012345678?token=AbC123XyZ ``` Render this URL as a QR code with any QR library. When a user scans it, their device opens the link, downloads the latest `.pkpass`, and Wallet prompts them to add it—which also registers the device for updates. ## Object reference ### Pass object | Field | Type | Description | | :------------------------------------------------------------------ | :----------- | :------------------------------------------------------------------------------------ | | `formatVersion` | integer | Pass format version. Defaults to `1`. | | `passTypeIdentifier` | string | Apple Pass Type ID (`pass.com.yourcompany.passtype`). Defaults from the certificate. | | `serialNumber` | string | Assigned by the server on create; identifies the pass. | | `teamIdentifier` | string | Apple Team ID. Defaults from the certificate. | | `organizationName` | string | Organization shown on the pass. Defaults from the certificate. | | `description` | string | Human-readable description (required by Apple). | | `boardingPass` / `coupon` / `eventTicket` / `storeCard` / `generic` | object | The pass style. **Exactly one** must be set. See [field groups](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#field-group-object). | | `backgroundColor` | string | Background color, `rgb(r, g, b)`. | | `foregroundColor` | string | Foreground (text) color, `rgb(r, g, b)`. | | `labelColor` | string | Field label color, `rgb(r, g, b)`. | | `logoText` | string | Text shown next to the logo. | | `suppressStripShine` | boolean | Disable the shine effect on the strip image. | | `barcodes` | array | [Barcodes](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#barcode-object) shown on the pass. | | `locations` | array | [Locations](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#location-object) that make the pass relevant. | | `beacons` | array | [Beacons](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#beacon-object) that make the pass relevant. | | `relevantDate` | string | ISO 8601 date when the pass becomes relevant. | | `maxDistance` | integer | Max distance (meters) for location relevance. | | `expirationDate` | string | ISO 8601 expiration date. | | `voided` | boolean | Marks the pass as void. | | `groupingIdentifier` | string | Groups related passes (event tickets/boarding passes). | | `userInfo` | object (map) | Arbitrary key/value app data. | Note `authenticationToken` and `webServiceUrl` are managed by the service and don’t need to be set by API clients. ### Field group object Each pass style (`boardingPass`, `coupon`, `eventTicket`, `storeCard`, `generic`) groups [fields](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#field-object) into areas: | Field | Type | Description | | :---------------- | :---- | :--------------------------------------------------------- | | `headerFields` | array | Shown in the pass header (visible when stacked in Wallet). | | `primaryFields` | array | Most prominent fields. | | `secondaryFields` | array | Below the primary fields. | | `auxiliaryFields` | array | Additional fields below secondary. | | `backFields` | array | Shown on the back of the pass. | `boardingPass` additionally has `transitType` (`PKTransitTypeAir`, `PKTransitTypeTrain`, `PKTransitTypeBus`, `PKTransitTypeBoat`, or `PKTransitTypeGeneric`). ### Field object | Field | Type | Description | | :------------------------ | :--------------- | :------------------------------------------------------------------ | | `key` | string | Unique field key within the pass. | | `label` | string | Field label. | | `value` | string | Field value (text or number as string). | | `changeMessage` | string | Message shown when the value changes (use `%@` as the placeholder). | | `textAlignment` | string | `PKTextAlignment` value. | | `dateStyle` / `timeStyle` | string | `PKDateStyle` for date/time formatting. | | `isRelative` | boolean | Show the date relative to now. | | `numberStyle` | string | `PKNumberStyle` for number formatting. | | `currencyCode` | string | ISO 4217 currency code. | | `dataDetectorTypes` | array of strings | Data detectors to apply to the value. | ### Barcode object | Field | Type | Description | | :---------------- | :----- | :------------------------------------------------------------------------------------------------- | | `format` | string | `PKBarcodeFormatQR`, `PKBarcodeFormatPDF417`, `PKBarcodeFormatAztec`, or `PKBarcodeFormatCode128`. | | `message` | string | Data encoded in the barcode. | | `messageEncoding` | string | Text encoding, typically `iso-8859-1`. | | `altText` | string | Text shown below the barcode. | ### Location object | Field | Type | Description | | :------------- | :----- | :------------------------------------------------ | | `latitude` | number | Latitude. | | `longitude` | number | Longitude. | | `altitude` | number | Altitude in meters. | | `relevantText` | string | Text shown on the lock screen near this location. | ### Beacon object | Field | Type | Description | | :-------------- | :------ | :---------------------------------------------- | | `proximityUuid` | string | iBeacon proximity UUID. | | `major` | integer | Major value. | | `minor` | integer | Minor value. | | `relevantText` | string | Text shown on the lock screen near this beacon. | ### Pass image object | Field | Type | Description | | :------------ | :-------------- | :--------------------------------------------------------------------------------------------------- | | `imageType` | string | One of `icon`, `logo`, `strip`, `background`, `footer`, `thumbnail`. `icon` and `logo` are required. | | `data` | string (Base64) | Image bytes. | | `contentType` | string | MIME type, for example `image/png`. | ### Pass record object Returned by list/get endpoints. | Field | Type | Description | | :------------------- | :------ | :------------------------------------------------- | | `serialNumber` | string | Pass serial number. | | `passTypeIdentifier` | string | Pass Type ID. | | `organizationName` | string | Organization name. | | `description` | string | Pass description. | | `createdAt` | string | Creation timestamp (RFC 3339). | | `updatedAt` | string | Last update timestamp (RFC 3339). | | `updateTag` | integer | Current update tag. | | `pass` | object | The full [pass object](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#pass-object), for editing. | | `userId` | string | Pushwoosh User ID the pass was issued to. | --- # Applications API **URL:** https://docs.pushwoosh.com/developer/api-reference/applications/ Create, update, delete, and manage applications using API methods for app configuration and settings. --- The Applications API manages the Pushwoosh applications on your account. Use it to create, update, delete, and list applications, download their configuration files, and turn platforms on or off. To set platform credentials after you create an application, use the [App configuration API](https://docs.pushwoosh.com/developer/api-reference/app-configuration/). ## createApplication `POST` `https://api.pushwoosh.com/json/1.3/createApplication` Creates a new application on the account. #### Request Body | Name | Type | Description | | ---------------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | authorize_all_tokens | boolean | Allows all API tokens to communicate with this app. | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | icon | string | Base64-encoded binary content of png file. | | sdk_ios | string | iOS SDK type. Possible values: “pw” or “corona”. For Corona, push payload is different. | | sdk_android | string | Android SDK type. Possible values: “pw”, “corona”, “phonegap”. AppPresser users select “phonegap”. | | title* | string | Title for the new app in Pushwoosh. | Example response: ```json { "status_code": 200, "status_message": "OK", "response": { "application": "1DC69-73EDB" } } ``` Example code: ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "icon": "iVBORw0KGgoAAAANSUhEUgAAA...", // optional. Base-64 encoded binary content of png file "authorize_all_tokens": true, // optional. If true, allows all api tokens to communicate with this app "sdk_ios": "pw", // optional. Possible values are "pw" or "corona" "sdk_android": "pw", // optional. Possible values are "pw", "corona", or "phonegap" "title": "APP_TITLE" // required. Title for the new app in Pushwoosh } } ``` ## updateApplication `POST` `https://api.pushwoosh.com/json/1.3/updateApplication` Updates the application settings. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | icon | string | Base64-encoded binary content of png file. | | sdk_ios | string | iOS SDK type. Possible values: “pw” or “corona”. For Corona, push payload is different. | | sdk_android | string | Android SDK type. Possible values: “pw”, “corona”, “phonegap”. AppPresser users select “phonegap”. | | title | string | New title for the app in Pushwoosh. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. application code of the app you want to update "icon": "iVBORw0KGgoAAAANSUD6...", // optional. Base-64 encoded binary content of png file "sdk_ios": "pw", // optional. Possible values are "pw" or "corona" "sdk_android": "pw", // optional. Possible values are "pw", "corona", or "phonegap" "title": "APP_TITLE" // optional. New title for the app in Pushwoosh } } ``` ## deleteApplication `POST` `https://api.pushwoosh.com/json/1.3/deleteApplication` Deletes the app from the account. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX" // required. Pushwoosh application code } } ``` ## getApplication `POST` `https://api.pushwoosh.com/json/1.3/getApplication` Gets details about the app. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "application": { "adm_cid": "", "adm_secret": "", "android_api_type": "x31", "android_gcm_api_key": "x7836363631366236353566366236353739", "android_sdk": "x7077", "icon_url": null, "ios_gateway": "x783330", "ios_key_management_type": "x783631373537343666", "ios_key_password": "x7836353336363236353335333736353333", "ios_sdk": "x7077", "title": "0 Application For test", "providers": { "ios": true, "amazon": false, "android": false, "chrome": false, "email": false, "firefox": false, "huawei_android": false, "internet_explorer": false, "osx": false, "safari": false, "sms": false, "web": false, "whats_app": false, "windows": false }, "languages": { "en": "34", "zh": "12", "sv": "7", "de": "6", "pt": "6" } } } } ``` ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX" // required. Pushwoosh application code } } ``` ## getApplications `POST` `https://api.pushwoosh.com/json/1.3/getApplications` Gets the list of applications on the account. Can return result with pagination. #### Request Body | Name | Type | Description | | ------ | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | page | integer | The page number for pagination. | #### Example request ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "page": 2 // optional. The page number for pagination } } ``` #### Response If there are more than 100 applications in the account, the response includes `current page` and `total amount of pages`. #### Example Response ```json { "status_code": 200, "status_message": "OK", "response": { "page": 1, // Current page "total": 2, // Total amount of pages "applications": [{ "APPLICATION_CODE": "MyApp1" }, { "APPLICATION_CODE": "MyApp2" }] } } ``` ## getApplicationFile `POST` `https://api.pushwoosh.com/json/1.3/getApplicationFile` Gets the configuration files related to the app. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | file* | string | Identifier of the file to retrieve. See values below. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "file": "ios_auto_privatekey" // required. identifier of the file to retrieve. See values below } } ``` #### Possible values for the “file” parameter: `“ios_auto_privatekey” | “ios_manual_privatekey” | “ios_auto_certificate” | “ios_manual_certificate” | “ios_push_certificate” | “ios_provisioning_profile” | “macos_manual_privatekey” | “macos_manual_certificate” | “macos_push_certificate” | “safari_manual_privatekey” | “safari_push_certificate” | “safari_push_package” | **Response example**: ```plaintext -----BEGIN CERTIFICATE----- ... -----END CERTIFICATE----- -----BEGIN RSA PRIVATE KEY----- ... -----END RSA PRIVATE KEY----- ``` ## setApplicationPlatformStatus `POST` `https://api.pushwoosh.com/json/1.3/setApplicationPlatformStatus` Changes the platform’s status of the app. #### Request Body | Name | Type | Description | | ------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | platform* | integer | The platform type. See platform types list below. | | status* | string | ”enable” or “disable” | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "message": "iOS pushes are disabled", "available_platforms": { "iOS": false, "Android": true, "OS X": false, "Windows": false, "Amazon": false, "Safari": false, "Chrome": true, "Firefox": false, } } } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "platform": 1, // required. 1 — iOS; 3 — Android; 7 — Mac OS X; 8 — Windows; // 9 — Amazon; 10 — Safari; 11 — Chrome; 12 — Firefox; "status": "disable" // required. "disable" or "enable" } } ``` --- # Audience API **URL:** https://docs.pushwoosh.com/developer/api-reference/audience-api/ Register and tag multiple devices in one request with the Audience API. Use request IDs to track registration and tag updates. --- The Audience API registers, unregisters, and tags devices in bulk. Each bulk method accepts a list of devices in one request, processes it in the background, and returns a `request_id`. Pass that `request_id` to the matching status method to check the result. Use bulk methods for imports and large tag updates, and the [Device API](https://docs.pushwoosh.com/developer/api-reference/device-api/) for a single device. Note The `request_id` used throughout this page is a job token for polling a `bulk*` operation’s status, unrelated to the message [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id) used for HTTP-level tracing. ## bulkSetTags `POST` `https://api.pushwoosh.com/api/v2/audience/bulkSetTags` Sets tag values for the list of devices. Important When using the `bulkSetTags` method, ensure that tag values are set for a minimum of 50 devices. To set tags for a single device, use the [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) method. #### Request Body | Name | Type | Description | | ---------------------- | ------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | application* | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | auth* | String | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | create_missing_tags | Boolean | If true, missing tags are created automatically. | | devices* | Object | Array of devices. | | devices.hwid | String | Can be used to identify a device instead of user_id or push_token. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) | | devices.user_id | String | Can be used to identify a user instead of hwid or push_token. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) | | devices.push_token | String | Can be used to identify a device instead of hwid or user_id. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token) | | devices.list_operator | String | Defines how to set values for [tags](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag) of the list type: set, append, or remove | | devices.tags* | Object | Values to set for the tags specified. | * OK ```json { "request_id": "request_id to use in GET method to obtain job status", "status": "Pending" } ``` * Error ```json { "message": "invalid request" } ``` Request: ```json { "application": "application code", // required. Pushwoosh app code "auth": "Pushwoosh auth token", // required. API access token from Pushwoosh Control Panel "create_missing_tags": false, // optional. Should automatically create missing tags "devices": [{ // required. Array of devices "hwid": "device hwid", // optional. Can be used to identify a device instead of // "user_id" or "push_token". "user_id": "user ID", // optional. Can be used to identify a user instead of "hwid" or "push_token". "push_token": "device push token", // optional. Can be used to identify a device instead of "hwid" or "user_id". "list_operator": "set", // required. For list tags. Defines how to set values for // tags of the list type: set, append, or remove "tags": { // required. Values to set for the tags specified. "tag_name": "tagvalue", // use correct value type "tag_name2": "tagvalue2" } }] } ``` Response: ```json { "request_id": "request_id to use in GET method to obtain job status", "status": "Pending" } ``` ## bulkSetTags status `GET` `https://api.pushwoosh.com/api/v2/audience/bulkSetTags/{request_id}?detailed=false` Returns the status of `/bulkSetTags` operation #### Path Parameters | Name | Type | Description | | ----------- | ------ | -------------------------------------------- | | request_id | String | request id from previous `/bulkSetTags` call | #### Query Parameters | Name | Type | Description | | -------- | ------- | ------------------------------------------------------- | | detailed | Boolean | (true/false) whether to return detailed per-device info | Response: ```json { "request_id": "id of the request", "status": "Completed", // also "Pending", "Failed" "progress": 100, // jobs progress 0-100 "devices_success": 100, // good devices "devices_not_found": 0, // devices not found in Pushwoosh "devices_failed": 0, // errored "devices": [{ // device report (only in detailed = true) "hwid": "device hwid", "status": "done", // also "failed", "not found" "tags": { "tagName": "ok", "tagName2": "tag not found", "tagName3": "wrong value. expect :string" } }] } ``` ## bulkRegisterDevice Registers multiple devices in Pushwoosh in a single request. It also allows specifying various tags for each device. `POST` `https://api.pushwoosh.com/api/v2/audience/bulkRegisterDevice` ### Request body parameters | Parameter | Type | Required | Description | | :---------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | application | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | auth | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token). | | devices | array | Yes | An array of device objects. Each object represents a device and its associated data. See details in the **Device object parameters** table below. | #### Device object parameters | Parameter | Type | Required | Description | | -------------- | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | hwid | string | Yes | [The hardware ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) or unique identifier for the device. | | push_token | string | Yes | [Push token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token) for the device. | | platform | integer | Yes | The platform identifier. [Learn more](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) | | list_operator | string | No | Determines the action for list-type tags: - **“append”**: Add the specified value to the tag list. - **“remove”**: Remove the specified value from the tag list. **Note**: If the `list_operator` parameter is not specified, all existing values in the tag list will be replaced with the provided values. | | tags | object | No | Custom [tags](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag) assigned to the device. Tags are key-value pairs used for segmentation. | #### Request example ```json { "application": "application code", // required. Pushwoosh app code "auth": "Pushwoosh auth token", // required. API access token from Pushwoosh Control Panel "devices": [{ // required. Array of devices "hwid": "device hwid", // required. Unique identifier for the device (can be an email). "push_token": "device push token", // required. Push notification token for the device. "platform": 14, // required. Device platform (e.g., 14 for email). "list_operator": "append", // optional. For list tags. Adds or removes the specified value(s) from the tag of list type. "tags": { // optional. Values to set for the specified tags. "language": "en", // use correct value type. "CSV_Import": "summer_camp" } }, { "hwid": "device hwid 2", // required. Unique identifier for the second device. "push_token": "device push token 2", // required. Push notification token for the device. "platform": 14, // required. Device platform. "list_operator": "remove", // optional. Adds or removes values from tags of the list type. "tags": { // optional. Values to remove from the specified tags. "language": "en", "CSV_Import": "summer_camp2" } }, { "hwid": "device hwid 3", // required. Unique identifier for the third device. "push_token": "device push token 3", // required. Push notification token for the device. "platform": 14, // required. Device platform. "tags": { // optional. Values to set for the specified tags. "language": "en", "CSV_Import": "summer_camp3" } }] } ``` ### Response The method responds with an operation ID, which can be used to track the status and results of the bulk registration process. ```json { "request_id": "request_id to use in GET method to obtain job status", "status": "Pending" } ``` ## bulkRegisterDevice status You can check the status of a bulk registration process by making the following **GET** request: `GET` `https://api.pushwoosh.com/api/v2/audience/bulkRegisterDevice/{request_id}?detailed=true` | Parameter | Type | Required | Description | | ----------- | ------- | -------- | ------------------------------------------------------------------------------------ | | request_id | string | Yes | The request ID returned by the POST request. | | detailed | boolean | No | If set to `true`, the response includes detailed results for each registered device. | #### Example response ```json { "request_id": "9a2e1a14-XXXX-46c3-XXXX-c254b25d3782", "status": "Completed", "progress": 100, "devices_success": 4, "devices": [ { "hwid": "user1@example.com", "status": "done" }, { "hwid": "user2@example.com", "status": "done" }, { "hwid": "user3@example.com", "status": "done" }, { "hwid": "invalid_email@example.com", "status": "failed" } ] } ``` ## bulkUnregisterDevice Unregisters multiple devices from Pushwoosh in a single request. `POST` `https://api.pushwoosh.com/api/v2/audience/bulkUnregisterDevice` ### Request body parameters | Parameter | Type | Required | Description | | :---------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | application | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | auth | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) | | devices | array | Yes | An array of device objects. Each object represents a device and its associated data. See details in the **Device object parameters** table below. | #### Device object parameters | Parameter | Type | Required | Description | | --------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------ | | hwid | string | Yes | The hardware ID or unique identifier for the device. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) | #### Request example ```json { "application": "application code", // required. Pushwoosh app code "auth": "Pushwoosh auth token", // required. API access token from Pushwoosh Control Panel "devices": [{ // required. Array of devices "hwid": "device hwid", // required. Unique identifier for the device (can be an email). }, { "hwid": "device hwid 2", // required. Unique identifier for the second device. }, { "hwid": "device hwid 3", // required. Unique identifier for the third device. }] } ``` ### Response The method responds with an operation ID, which can be used to track the status and results of the bulk process. ```json { "request_id": "request_id to use in GET method to obtain job status", "status": "Pending" } ``` ## bulkUnregisterDevice status You can check the status of a bulk un-registration process by making the following **GET** request: `GET` `https://api.pushwoosh.com/api/v2/audience/bulkUnregisterDevice/{request_id}?detailed=true` | Parameter | Type | Required | Description | | ----------- | ------- | -------- | -------------------------------------------------------------------------------------- | | request_id | string | Yes | The request ID returned by the POST request. | | detailed | boolean | No | If set to `true`, the response includes detailed results for each unregistered device. | #### Example response ```json { "request_id": "9a2e1a14-XXXX-46c3-XXXX-c254b25d3782", "status": "Completed", "progress": 100, "devices_success": 4, "devices": [ { "hwid": "user1@example.com", "status": "done" }, { "hwid": "user2@example.com", "status": "done" }, { "hwid": "user3@example.com", "status": "done" }, { "hwid": "invalid_email@example.com", "status": "failed" } ] } ``` --- # Campaigns API **URL:** https://docs.pushwoosh.com/developer/api-reference/campaigns/ Create and delete campaigns using API methods to organize and track push notification campaigns. --- The Campaigns API creates, lists, and deletes campaigns in a Pushwoosh application. To get statistics for a campaign, use [`/getCampaignStats`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/). ## createCampaign `POST` `https://api.pushwoosh.com/json/1.3/createCampaign` Creates a push campaign within the app. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | name* | string | Campaign name. | | description | string | Campaign description. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "campaign": "XXXXX-XXXXX" } } ``` ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "name": "CAMPAIGN_NAME", // required. "description": "CAMPAIGN_DESCRIPTION" // optional. } } ``` ## deleteCampaign `POST` `https://api.pushwoosh.com/json/1.3/deleteCampaign` Deletes a particular campaign. #### Request Body | Name | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | campaign* | string | [Code of a campaign](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to delete. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "campaign": "XXXXX-XXXXX" // required. ID of a campaign to delete } } ``` ## getCampaigns `POST` `https://api.pushwoosh.com/json/1.3/getCampaigns` Retrieves the list of campaigns for the app. #### Request Body | Name | Type | Description | | ------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | cursor | string | Indicates the last campaign retrieved in the previous request. | | limit | integer | The max number of campaigns for a single response. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "campaigns": [{ "code": "7CFF3-63A7B", "name": "CAMPAIGN_NAME_1", "description": "DESCRIPTION1" }, { "code": "C284C-33DD3", "name": "CAMPAIGN_NAME_2", "description": "DESCRIPTION2" }], "limit": 100, "cursor": "+4PxJPhhwITD4PFsiKui1BA==" } } ``` Example ```json { "request": { "auth": "jLbec2UW........ztFSOf5Za", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "cursor": "+4PxJPhhwITD4PFsiKui1BA==", // optional. Indicates the last campaign retrieved in the previous request. "limit": 100 // optional. The max number of campaigns // for a single response. } } ``` --- # Control Groups API **URL:** https://docs.pushwoosh.com/developer/api-reference/control-groups-api/ Create, inspect, resize, and check membership of Pushwoosh control groups (global hold-out audiences) through a REST API. --- A [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) is a hold-out share of an application’s users that never receives marketing messages, so the effect of messaging can be measured against it. This API manages control groups and answers per-user membership. Use it to mirror the Control Panel’s **Settings > Control groups** actions from your own systems, or to check whether specific user IDs are held out before a send or an import. ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise. ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` ## Conventions * **Field naming:** request bodies and query/path parameters accept `lowerCamelCase` (for example, `controlGroupCode`, `userIds`), and the server unmarshals either casing. Responses are always marshaled using the proto field names, in `snake_case` (`application_id`, `in_control_group`, and so on). The response examples and the [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object) reference below use that casing. * **`code`:** every control group response carries its own code, generated on `Create`. Pass this code as `controlGroupCode` to `Get`, `UpdatePercentage`, `UpdateCountries`, `Rename`, `Disable`, `Reshuffle`, `ForceUpdateCalculation`, `GetCalculationStatus`, `GetAnalytics`, and `CheckControlGroupMembership`. * **Several groups:** an application can have several control groups. Every enabled group holds users out of every marketing send within its own countries and tag, independently of the other groups, and groups may overlap. A send doesn’t select a group. The group with an empty `name` is the application’s original group and works the same way. ### Error responses | HTTP status | Meaning | | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400 Bad Request` | Invalid argument, such as `percentage` outside 1–20, `userIds` empty or over 1,000 entries, an unrecognized country code, `scopeValues` set without `scopeTag`, `scopeTag` naming a tag the account doesn’t have, or a `scopeValues` entry [`UpdateSettings`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatesettings) rejects (see below). Also returned (as a `FailedPrecondition` on the wire) by `UpdatePercentage`, `UpdateCountries`, `UpdateSettings`, `Disable`, `Reshuffle`, and `Delete` on a control group that belongs to a campaign’s own hold-out, per the [caution](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatepercentage) below. `Reshuffle` alone also refuses this way on a disabled (`percentage` 0) group. | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application or control group does not belong to the caller’s account. | | `404 Not Found` | The control group or application was not found. | | `409 Conflict` | `Create` used a `name` that already exists in the application. | | `500 Internal Server Error` | Unexpected server-side failure. | ## Endpoints | Method | Path | Description | | :------- | :-------------------------------------------------------------------------------- | :--------------------------------------------------------------- | | `GET` | `/api/applications/{code}/control_groups` | List an application’s control groups | | `POST` | `/api/applications/{code}/control_groups` | Create a control group | | `GET` | `/api/applications/{code}/control_groups/{control_group_code}` | Get a single control group | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}` | Resize a control group | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/countries` | Rescope a control group to a set of countries | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/settings` | Apply size, country and tag scope, and lifetime mode in one call | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/display_name` | Rename a control group | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/disable` | Disable a control group | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/reshuffle` | Reshuffle a control group | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/recalculate` | Force-recalculate a control group’s size | | `GET` | `/api/applications/{code}/control_groups/{control_group_code}/calculation_status` | Poll a running size calculation | | `GET` | `/api/applications/{code}/control_groups/{control_group_code}/analytics` | Get Control-vs-Treatment analytics | | `GET` | `/api/applications/{code}/control_groups/{control_group_code}/cycles` | List the group’s closed cycles | | `POST` | `/api/applications/{code}/control_groups/{control_group_code}/membership` | Check membership for a batch of user IDs | | `DELETE` | `/api/applications/{code}/control_groups/{control_group_code}` | Delete a control group | ## List Lists every control group configured for an application, the unnamed original one first. `GET` `/api/applications/{code}/control_groups` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :------------------------------------------------------------------------------------------------------------- | | `code` | string | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) to list control groups for. | ### Response | Field | Type | Description | | :--------------- | :------------------------------------------------------ | :-------------------------------------------------- | | `control_groups` | array of [Control group objects](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object) | Every control group configured for the application. | ## Create Creates a named control group for an application and returns it with its generated code. The new group holds users out of every marketing send within its own countries and tag, alongside the application’s other enabled groups. `POST` `/api/applications/{code}/control_groups` ### Request body | Parameter | Type | Required | Description | | :------------ | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `code` | string | Yes | The application code to create the group in. | | `name` | string | Yes | Group name, up to 64 characters and without a colon. Must be unique within the application. Part of the membership key, so it never changes. | | `percentage` | integer | Yes | Hold-out size as a percentage, 1–20. | | `segment` | string | No | [Seglang](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) expression to measure the group over. Omit to measure over the whole base. | | `countries` | array of strings | No | Lowercase ISO-3166-1 alpha-2 country codes to scope the hold-out to. Omit for every country. Codes are case-insensitive on input. | | `scopeTag` | string | No | A string or boolean tag to scope the hold-out to, AND-ed with `countries`. Omit for no tag scope. See [Tag scope](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#tag-scope) below. | | `scopeValues` | array of strings | See note | Values of `scopeTag` that put a user in scope. Required if `scopeTag` is set, and must be empty otherwise. | | `displayName` | string | No | Name the Control Panel shows, up to 64 characters. Omit to show `name`. | ##### Request example ```json { "code": "XXXXX-XXXXX", "name": "Q3 holdout", "percentage": 10 } ``` ### Response Returns `{ "group": { ... } }`, the new [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object). ## Get Returns one control group by its code, with its hold-out size, generation, and the user counts behind it. `GET` `/api/applications/{code}/control_groups/{control_group_code}` ### Path parameters | Parameter | Type | Description | | :------------------- | :----- | :----------------------------------------- | | `code` | string | The application code the group belongs to. | | `control_group_code` | string | The control group’s code. | ### Response | Field | Type | Description | | :-------------------- | :-------------------------------------------- | :-------------------------------------------------------------------------------- | | `group` | [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object) | The requested control group. | | `total_users` | integer | All users in the application. | | `control_group_users` | integer | Users currently held out. | | `calculation_status` | string | `TASK_STATUS_NOT_STARTED`, `TASK_STATUS_IN_PROGRESS`, or `TASK_STATUS_COMPLETED`. | | `has_data` | boolean | Whether cached size numbers are available yet. | ## UpdatePercentage Sets the hold-out percentage (1–20) of one control group. Resizing keeps every existing member: the hold-out grows or shrinks around them rather than being redrawn. Superseded by [UpdateSettings](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatesettings), which applies size, scope, and lifetime mode in one call, but still supported. `POST` `/api/applications/{code}/control_groups/{control_group_code}` Caution A campaign can create its own hold-out sample, which is a control group too but doesn’t appear in [List](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#list). This endpoint, [UpdateCountries](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatecountries), [UpdateSettings](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatesettings), [Disable](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#disable), [Reshuffle](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#reshuffle), and [Delete](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#delete) all refuse to touch one, returning `400 Bad Request` (“control group belongs to a campaign and cannot be changed”) instead. ### Request body | Parameter | Type | Required | Description | | :----------- | :------ | :------- | :--------------------------------------- | | `percentage` | integer | Yes | New hold-out size as a percentage, 1–20. | ### Response An empty object on success: `{}`. ## UpdateCountries Sets the countries one control group is scoped to. Changing the scope restarts the uplift measurement, because the population being compared changes. The hold-out itself is not redrawn. Superseded by [UpdateSettings](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatesettings), which also sets a [tag scope](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#tag-scope), but still supported. `POST` `/api/applications/{code}/control_groups/{control_group_code}/countries` ### Request body | Parameter | Type | Required | Description | | :---------- | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------- | | `countries` | array of strings | Yes | Lowercase ISO-3166-1 alpha-2 country codes. An empty list widens the group back to every country. Codes are case-insensitive on input. | ##### Request example ```json { "countries": ["us", "ca", "gb"] } ``` ### Response An empty object on success: `{}`. ## UpdateSettings Applies a control group’s size, country and tag scope, and lifetime mode in one call. A change that alters who is held out or how long for (size, scope, mode, refresh period, or end date) closes the running cycle and starts a new one. Sending the current values changes nothing. Refuses a control group that belongs to a campaign’s own hold-out, the same way as [UpdatePercentage](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatepercentage). `POST` `/api/applications/{code}/control_groups/{control_group_code}/settings` ### Request body | Parameter | Type | Required | Description | | :------------------ | :---------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------- | | `percentage` | integer | Yes | Hold-out size as a percentage, 1–20. | | `countries` | array of strings | No | Lowercase ISO-3166-1 alpha-2 country codes. An empty list widens the group back to every country. | | `scopeTag` | string | No | A string or boolean tag to scope the hold-out to, AND-ed with `countries`. Empty removes the tag scope. See [Tag scope](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#tag-scope) below. | | `scopeValues` | array of strings | See note | Values of `scopeTag` that put a user in scope. Required if `scopeTag` is set, and must be empty otherwise. | | `mode` | string | No | Lifetime of the membership: `CONTROL_GROUP_MODE_PERMANENT` (default), `CONTROL_GROUP_MODE_AUTO_REFRESH`, or `CONTROL_GROUP_MODE_EXPERIMENT`. | | `refreshPeriodDays` | integer | See note | Days between redraws, 7–365. Required with `CONTROL_GROUP_MODE_AUTO_REFRESH`, and must be omitted otherwise. | | `endsAt` | string (RFC 3339) | See note | When an experiment switches off, at least 30 days ahead. Required with `CONTROL_GROUP_MODE_EXPERIMENT`, and must be omitted otherwise. | Every field is applied as sent, the same as [UpdateCountries](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatecountries): an empty `countries` widens the group back to every country, and an empty `scopeTag` removes the tag scope. ### Response Returns `{ "group": { ... } }`, the updated [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object). ## Tag scope A control group can hold out only users whose value of one string or boolean tag is in a set you choose, AND-ed with `countries` if both are set. Set it with [Create](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#create) or [UpdateSettings](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#updatesettings). * `scopeTag` names the tag; empty means no tag scope. It can’t be `Country`: scope by country uses `countries`, not a tag. * `scopeValues` lists which values of `scopeTag` are in scope. At least one is required when `scopeTag` is set, and none can repeat. A string tag’s values can’t be empty strings. A boolean tag’s values must each be `"true"` or `"false"`. * A device with no value for `scopeTag` is out of scope, the same as a device with no `Country` tag. * Membership itself doesn’t change: the [formula that assigns users](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-control-group-membership-works) is untouched by scope. Tag and country scope are both a per-device check on top of it, narrowing which of a selected user’s devices are actually excluded, not who the formula selects. * A user-level tag is copied onto every one of that user’s devices when it’s set, so the device-level check above already covers user-level tags too, not only device-level ones. * Changing `scopeTag`, or changing `scopeValues` as a set (reordering alone doesn’t count), closes the running cycle with `CONTROL_GROUP_CYCLE_CLOSE_REASON_RESCOPED`, the same as changing `countries`. Disabling a group keeps its tag scope, as it already keeps `countries`. ## Rename Sets the name the Control Panel shows for one control group. `name` is part of the membership key and doesn’t change, so the group keeps the same users and its running cycle. `POST` `/api/applications/{code}/control_groups/{control_group_code}/display_name` ### Request body | Parameter | Type | Required | Description | | :----------------- | :----- | :------- | :---------------------------------------------------------------------------------------------------------- | | `code` | string | Yes | The application code the group belongs to. | | `controlGroupCode` | string | Yes | The control group’s code. | | `displayName` | string | No | New name, up to 64 characters and unique within the application. Send an empty string to show `name` again. | ### Response Returns `{ "group": { ... } }`, the renamed [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object). ## Disable Switches one control group off, keeping it and its generation so that switching it back on restores the same hold-out rather than drawing a new one. `POST` `/api/applications/{code}/control_groups/{control_group_code}/disable` ### Response An empty object on success: `{}`. ## Reshuffle Redraws one control group’s hold-out by incrementing its generation. This is the only way to get a different sample: membership is deterministic, so disabling and re-enabling reproduces exactly the same one. `POST` `/api/applications/{code}/control_groups/{control_group_code}/reshuffle` Caution Refuses a disabled group (`percentage` 0) with `400 Bad Request` (“control group is not enabled”). Reshuffling it would only move a hold-out nobody is in, and would silently consume a generation. ### Response | Field | Type | Description | | :----------- | :------ | :------------------------------------------ | | `generation` | integer | The group’s generation after the reshuffle. | ## ForceUpdateCalculation Starts a fresh count of one control group’s size. The previously cached numbers keep being served until the new count finishes. Poll [GetCalculationStatus](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#getcalculationstatus) for progress. `POST` `/api/applications/{code}/control_groups/{control_group_code}/recalculate` ### Response An empty object on success: `{}`. ## GetCalculationStatus Polls just the changing user counts of one control group while a size calculation runs. `GET` `/api/applications/{code}/control_groups/{control_group_code}/calculation_status` ### Response | Field | Type | Description | | :-------------------- | :------ | :-------------------------------------------------------------------------------- | | `total_users` | integer | All users in the application. | | `control_group_users` | integer | Users held out, counted over the application. | | `calculation_status` | string | `TASK_STATUS_NOT_STARTED`, `TASK_STATUS_IN_PROGRESS`, or `TASK_STATUS_COMPLETED`. | | `has_data` | boolean | Whether cached size numbers are available. | ## GetAnalytics Returns the precomputed Control-vs-Treatment analytics for one control group. Note A nightly job computes analytics for every enabled group, named or not. A group measured over a `segment` has no analytics yet. `GET` `/api/applications/{code}/control_groups/{control_group_code}/analytics` ### Query parameters | Parameter | Type | Required | Description | | :----------- | :----- | :------- | :----------------------------------------------------------------------------- | | `windowDays` | string | No | Lookback window preset: `WINDOW_DAYS_3`, `WINDOW_DAYS_7`, or `WINDOW_DAYS_30`. | ### Response | Field | Type | Description | | :------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `events` | array of objects | One entry per tracked event, each with `event`, `treatment` and `control` (`users`, `conversions`, `conversion_rate`, `events_per_user`), `uplift_pct`, `incremental_events`, `percent_of_treatment`, `z_score`, `p_value`, `confidence_pct`, and `significance` (`SIGNIFICANCE_NOT_ENOUGH_DATA`, `SIGNIFICANCE_NOT_SIGNIFICANT`, or `SIGNIFICANCE_SIGNIFICANT`). | ## ListControlGroupCycles Lists the closed cycles of one control group, newest first. Each cycle is the membership the group ran with between two settings changes, together with the settings it was drawn from. The running (current) cycle isn’t in this list. Its settings are on the [Control group object](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-object) itself. `GET` `/api/applications/{code}/control_groups/{control_group_code}/cycles` ### Response | Field | Type | Description | | :------- | :------------------------------------------------------------------ | :------------ | | `cycles` | array of [Control group cycle objects](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#control-group-cycle-object) | Newest first. | ### Control group cycle object | Field | Type | Description | | :------------------------ | :---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `cycle_number` | integer | Sequential within the group; the group’s own `cycle_number` is the one after the last closed here. | | `mode` | string | Lifetime mode the group ran in during this cycle: `CONTROL_GROUP_MODE_PERMANENT`, `CONTROL_GROUP_MODE_AUTO_REFRESH`, or `CONTROL_GROUP_MODE_EXPERIMENT`. | | `generation` | integer | The group’s generation during this cycle. | | `percentage` | integer | Hold-out size during this cycle. | | `countries` | array of strings | Country scope during this cycle; empty is every country. | | `started_at` / `ended_at` | string (RFC 3339) | When this cycle ran. | | `close_reason` | string | Why the cycle ended: `CONTROL_GROUP_CYCLE_CLOSE_REASON_ROLLOVER`, `_EXPIRED`, `_RESHUFFLED`, `_RESIZED`, `_RESCOPED`, `_MODE_CHANGED`, or `_DISABLED`. | | `scope_tag` | string | Tag the cycle’s hold-out was scoped to, AND-ed with `countries`. Empty for no tag scope and for every cycle closed before tag scope existed, even if the group had one later. | | `scope_values` | array of strings | Values of `scope_tag` that put a user in scope during this cycle; only set together with `scope_tag`. | ## CheckControlGroupMembership Reports for each user ID whether it is held out by one control group right now. Membership is computed from the ID alone. No user record is read, so an ID the application never saw is answered too, and a disabled group answers `false` for every ID rather than an error. A group with a [country or tag scope](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#tag-scope) holds a user out only if one of their devices is in that scope, so an unseen ID (no device at all) comes back `false` there even though the same ID would come back `true` on an unscoped group. Use this instead of exporting the whole group to check the users of a specific send or import. `POST` `/api/applications/{code}/control_groups/{control_group_code}/membership` ### Request body | Parameter | Type | Required | Description | | :-------- | :--------------- | :------- | :----------------------------------------- | | `userIds` | array of strings | Yes | User IDs to check, at most 1,000 per call. | ##### Request example ```json { "userIds": ["user-1", "user-2", "user-3"] } ``` ### Response | Field | Type | Description | | :------ | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | | `users` | array of objects | One entry per requested ID, in the order they were given (duplicates included). Each has `user_id` (string) and `in_control_group` (boolean). | ##### Response example ```json { "users": [ { "user_id": "user-1", "in_control_group": false }, { "user_id": "user-2", "in_control_group": true }, { "user_id": "user-3", "in_control_group": false } ] } ``` ## Delete Removes a control group entirely. It stops holding users out, and its statistics can no longer be opened. The application’s other groups keep working. `DELETE` `/api/applications/{code}/control_groups/{control_group_code}` Caution This cannot be undone. To stop holding users out while keeping the group, its generation, and its history, [disable](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#disable) it instead. ### Response An empty object on success: `{}`. ## Control group object | Field | Type | Description | | :----------------- | :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `code` | string | Control group code (format `XXXXX-XXXXX`), stable for the life of the group. | | `name` | string | Group name, part of the membership key. Empty is the application’s original, unnamed group. | | `display_name` | string | Name the Control Panel shows. Empty shows `name`, and the unnamed group as Global. | | `segment` | string | Seglang expression the group is measured over; empty is the whole base. | | `percentage` | integer | Hold-out size as a percentage, 1–20. Zero means the group is off. | | `enabled` | boolean | Whether the group is currently holding users out. | | `generation` | integer | Incremented by every reshuffle; `0` means never reshuffled. | | `last_modified_at` | string (RFC 3339) | When the group’s settings were last changed. | | `last_modified_by` | string | Email of the user who last changed the group. | | `application_id` | integer | Numeric ID of the application, the first part of the membership key `:::` that [CheckControlGroupMembership](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#checkcontrolgroupmembership) hashes to decide whether a user is held out. | | `countries` | array of strings | Lowercase ISO-3166-1 alpha-2 country codes the hold-out is scoped to. Empty is every country. | | `scope_tag` | string | Tag the hold-out is scoped to, AND-ed with `countries`; empty is no tag scope. See [Tag scope](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#tag-scope). | | `scope_values` | array of strings | Values of `scope_tag` that put a user in scope; a boolean tag uses `"true"` and `"false"`. | ## Related [Control groups (product guide) ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) [Segmentation (Filters) API ](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/) --- # Customer Journey API overview **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/ REST/JSON API to create, update, and manage Customer Journeys, trigger API entries, and read journey statistics. --- The Customer Journey API lets a backend manage [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) programmatically: create and edit journey definitions, move journeys through their lifecycle (start, pause, finish, draft, archive), trigger a running journey from your own systems, and pull per-journey statistics. It is the same API the Customer Journey builder uses, exposed over REST/JSON through a gRPC-Gateway bridge. ## Base URL The gRPC-Gateway methods and the legacy external methods are served on different hosts: | Methods | Base URL | | -------------------------------------------------------------------------- | ------------------------------------------- | | gRPC-Gateway: `/api/v3/journeygateway/...` (lifecycle, create, update) | `https://journey-api.svc-nue.pushwoosh.com` | | Legacy external: `/api/journey/...` (start by API, statistics, drop users) | `https://journey.pushwoosh.com` | Tip If you use a dedicated region or private deployment, confirm the exact base URLs with your Pushwoosh Customer Success Manager. ## Authentication Every request must include an `Authorization` header with a server-side Pushwoosh [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` Note The token is bound to the account that owns it. All operations apply to that account. Use the same token you issue for other server-to-server API calls, and never expose it in client applications. ## Methods ### Manage journeys * [Lifecycle](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/): `POST /api/v3/journeygateway/{action}`. Start, pause, finish, draft, or archive a journey by its UUID. * [Create and update](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/): `POST /api/v3/journeygateway` and `PUT /api/v3/journeygateway/{uuid}`. Create a new journey definition or replace an existing one. ### Trigger journeys * [Start by API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/): `POST /api/journey/{id}/start/external`. Inject users into the API entry point of a journey that is already running. ### Statistics and audience * [Get Journey stats](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/): `GET /api/journey/{id}/statistics/external`. Per-point delivery and conversion metrics. * [Remove users from journeys](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/): `POST /api/journey/drop-users/external`. Drop users from all or selected active journeys. ### Reference * [Journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/): the shape of the journey definition (info, params, points, comments) returned by the lifecycle, create, and update methods. * [Point reference](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/): the `point_data` structure for each point type: entry, timing, splitting, action, and messaging elements. ## Lifecycle start vs Start by API Customer Journey has two operations that sound similar but behave differently. [Lifecycle start](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/#endpoints) changes the journey state (for example, from Draft to **Running**). [Start by API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) injects users into an already-running journey. The table below compares them side by side. | | Lifecycle Start | Start by API | | ---------------------- | ------------------------------------------------------------- | ------------------------------------------------------------------------ | | Endpoint | `POST /api/v3/journeygateway/start` | `POST /api/journey/{id}/start/external` | | What it does | Activates the journey and moves it into the **Running** state | Injects users into the **API entry point** of an already-running journey | | Journey state required | Draft or Paused | Running (with an API Start point) | | Run frequency | Once per state change | Repeatedly, as users need to enter | ## Request and response format * Content type: `application/json`. * The `v3` field names use `snake_case`. Enum values are serialized as their string names (for example, `"STATUS_RUNNING"`, `"POINT_TYPE_SEND_PUSH"`). * The gRPC-Gateway methods (`/api/v3/journeygateway/...`) return the [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) on success and the standard gRPC-Gateway error envelope on failure: `{ "code": ..., "message": ..., "details": [...] }`. * The legacy external methods (`/api/journey/...`) return a method-specific JSON body on success and `{ "success": false, "message": ... }` with HTTP `400` on validation errors. ## Quick start Start a journey ```bash curl -X POST https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/start \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "uuid": "11111111-2222-3333-4444-555555555555" }' ``` ## Next steps [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) [Create and update ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) [Start by API ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) [Get Journey stats ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) [Remove users from journeys ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/) [Journey object ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) [Point reference ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/) --- # Create and update **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/ Create a new Customer Journey definition or replace an existing one through the /api/v3/journeygateway endpoints. --- These endpoints create and edit the journey definition: its title, settings, and the points on the canvas. They do not start the journey. Use [Lifecycle](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) for that. Both accept and return the [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/). Tip Writing a full journey graph in JSON is hard. Each point has its own channel fields and conditions ([Point reference](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/)). The most reliable workflow is to build the journey once in the [Customer Journey builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), read it back from a create/update response, then use these endpoints to clone or adjust it programmatically. Before you send a journey you read back to [Create](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/#create), clear `campaign_code` and `campaign_uid` in its points, or Create rejects the request. Each point gets its own campaign when the journey starts. ## Create `POST` `https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway` Creates a new journey in the `STATUS_DRAFT` state. ### Request | Field | Required | Type | Description | | ---------- | -------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | | `title` | Yes | string | Journey name. | | `params` | Yes | [`JourneyParams`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeyparams) | Journey-wide configuration. `params.application_code` is required. | | `points` | No | array of [`Point`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point) | Journey elements on the canvas and their connections. | | `comments` | No | array of [`JourneyComment`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeycomment) | Canvas comments. | ##### Request example Create a journey ```bash curl -X POST https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Welcome series", "params": { "application_code": "XXXXX-XXXXX" }, "points": [], "comments": [] }' ``` ### Response Returns the new [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/). Read the journey’s UUID from `info.uuid` and use it for subsequent update and lifecycle calls. ##### Response example ```json { "info": { "uuid": "11111111-2222-3333-4444-555555555555", "title": "Welcome series", "status": "STATUS_DRAFT", "params": { "application_code": "XXXXX-XXXXX" } }, "points": [], "comments": [] } ``` ## Full journey example A complete `Create` body for a small welcome flow. The graph is: ```plaintext Start by segment ──default──▶ Send push ──default──▶ Wait 1 day ──default──▶ Boolean splitter ──output1──▶ Send email ──default──▶ Exit └──default───────────────────────────▶ Exit ``` Points are wired together through each output’s `info.next_point_uuid`. Note the [output keys](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#output-keys): every point uses `default`, except the boolean splitter, which adds `output1` for the matched-condition branch (`default` is its fallback). The exit point has no outputs — and is optional: leaving `info.next_point_uuid` unset on any other output ends the journey the same way. ```json { "title": "Welcome series", "params": { "application_code": "XXXXX-XXXXX", "user_id_track_change_policy": "TRACK" }, "points": [ { "uuid": "11111111-1111-1111-1111-111111111111", "title": "New signups", "point_type": "POINT_TYPE_START_BY_SEGMENT", "position": { "x": 80, "y": 200 }, "outputs": [ { "identity": { "key": "default", "order": 0 }, "info": { "title": "", "next_point_uuid": "22222222-2222-2222-2222-222222222222" } } ], "point_data": { "start_by_segment": { "application_code": "XXXXX-XXXXX", "filter_code": "AAAAA-BBBBB" } } }, { "uuid": "22222222-2222-2222-2222-222222222222", "title": "Welcome push", "point_type": "POINT_TYPE_SEND_PUSH", "position": { "x": 340, "y": 200 }, "outputs": [ { "identity": { "key": "default", "order": 0 }, "info": { "title": "", "next_point_uuid": "33333333-3333-3333-3333-333333333333" } } ], "point_data": { "send_push": { "application_code": "XXXXX-XXXXX", "title": "Welcome!", "content": "Thanks for joining — here's how to get started." } } }, { "uuid": "33333333-3333-3333-3333-333333333333", "title": "Wait 1 day", "point_type": "POINT_TYPE_WAIT", "position": { "x": 600, "y": 200 }, "outputs": [ { "identity": { "key": "default", "order": 0 }, "info": { "title": "", "next_point_uuid": "44444444-4444-4444-4444-444444444444" } } ], "point_data": { "delay": { "sec": 86400 } } }, { "uuid": "44444444-4444-4444-4444-444444444444", "title": "Opened the push?", "point_type": "POINT_TYPE_BOOLEAN_SPLITTER", "position": { "x": 860, "y": 200 }, "outputs": [ { "identity": { "key": "output1", "order": 1 }, "info": { "title": "Matched", "next_point_uuid": "55555555-5555-5555-5555-555555555555" } }, { "identity": { "key": "default", "order": 0 }, "info": { "title": "Everyone else", "next_point_uuid": "66666666-6666-6666-6666-666666666666" } } ], "point_data": { "boolean_splitter": { "split_strategy": "SPLIT_FILTER", "load_policy": "ALL", "filter_code": "CCCCC-DDDDD" } } }, { "uuid": "55555555-5555-5555-5555-555555555555", "title": "Follow-up email", "point_type": "POINT_TYPE_SEND_EMAIL", "position": { "x": 1120, "y": 120 }, "outputs": [ { "identity": { "key": "default", "order": 0 }, "info": { "title": "", "next_point_uuid": "66666666-6666-6666-6666-666666666666" } } ], "point_data": { "send_email": { "application_code": "XXXXX-XXXXX", "preset_code": "12345-67890" } } }, { "uuid": "66666666-6666-6666-6666-666666666666", "title": "Exit", "point_type": "POINT_TYPE_EXIT", "position": { "x": 1380, "y": 200 }, "outputs": [], "point_data": { "terminator": {} } } ], "comments": [] } ``` ## Update `PUT` `https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/{uuid}` Replaces the definition of an existing journey. Send the **complete** definition. The points and comments you submit replace the current ones. ### Path parameters | Name | Type | Description | | ------ | ------ | ----------------------------------------------------------------------------- | | `uuid` | string | [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) to update. | ### Request | Field | Type | Description | | ---------- | --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | `title` | string | New journey name. | | `params` | [`JourneyParams`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeyparams) | New journey-wide configuration. | | `points` | array of [`Point`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point) | New full set of points. | | `comments` | array of [`JourneyComment`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeycomment) | New full set of comments. | | `autosave` | bool | When `true`, the change is treated as an editor autosave. Defaults to `false`. | ##### Request example Rename and reconfigure a journey ```bash curl -X PUT https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/11111111-2222-3333-4444-555555555555 \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Welcome series v2", "params": { "application_code": "XXXXX-XXXXX" }, "points": [], "comments": [] }' ``` ### Response Returns the updated [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/). ## Update and resume `POST` `https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/{uuid}/resume` Applies edits to a journey and resumes it in a single call. This is the equivalent of an [Update](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/#update) followed by a lifecycle [Start](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/). The request body matches [Update](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/#update). Use this when editing a paused journey you want to put back into the `STATUS_RUNNING` state. ## Rename `POST` `https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/{uuid}/rename` Changes only the journey title, without touching its points or settings. | Field | Required | Type | Description | | ------- | -------- | ------ | ----------------- | | `title` | Yes | string | New journey name. | ##### Request example Rename a journey ```bash curl -X POST https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/11111111-2222-3333-4444-555555555555/rename \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "title": "Welcome series (Q3)" }' ``` ## Related [Journey object ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) [Start by API ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) --- # Remove users from journeys **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/ Remove a list of users from all or specific active Customer Journeys. --- `POST` `https://journey.pushwoosh.com/api/journey/drop-users/external` Removes a list of users from all or specific active journeys. ## Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------------------ | -------------------------------------------------------------------------------- | | Content-Type | Yes | `application/json` | | | Authorization | Yes | `Api ` | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). | ## Request body | Field | Required | Type | Description | | --------------- | ----------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------- | | `user_ids` | Yes | `string[]` | List of [User IDs](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to remove from journeys. | | `journey_uuids` | Required if `all_journeys` is not provided or `false` | `string[]` | List of [Journey IDs](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) to remove users from. | | `all_journeys` | Required if `journey_uuids` is not provided | `boolean` | If `true`, users are removed from all journeys. If `false`, `journey_uuids` must be provided. | Note At least one of `all_journeys` (set to `true`) or `journey_uuids` (non-empty array) must be provided. ### Request examples #### Remove users from all journeys ```json { "user_ids": ["user1", "user2", "user3"], "all_journeys": true } ``` #### Remove users from specific journeys ```json { "user_ids": ["user1", "user2"], "journey_uuids": ["journey-uuid-1", "journey-uuid-2"] } ``` ### Response example * 200 ```json { "success": true } ``` * 400 If the request is invalid, the API returns HTTP `400` with a descriptive `message`. ```json { "success": false, "message": "user_ids must be provided" } ``` ```json { "success": false, "message": "journey_uuids must be provided if all_journeys is false" } ``` ## Related [Get Journey stats ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) [Start by API ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) --- # Journey object **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/ Reference for info, params, points, and comments in the Customer Journey definition, as returned and accepted by v3 endpoints. --- The [lifecycle](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/), [create, and update](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) methods all return a journey object with the same top-level shape: Shape ```json { "info": { ... }, // read-only metadata (responses only) "params": { ... }, // journey-wide configuration (create / update) "points": [ ... ], // canvas nodes and their connections "comments": [ ... ] // canvas comments } ``` When you **create** or **update** a journey you send `title`, `params`, `points`, and `comments`. Responses return `info` (which contains `params`) plus `points` and `comments`. ## Info Read-only journey metadata. Returned by every v3 method. Not part of the request body. | Field | Type | Description | | -------------------- | -------------------------------------- | ------------------------------------------------------------------- | | `uuid` | string | [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id). | | `title` | string | Journey name. | | `status` | [`JourneyStatus`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeystatus-enum) | Current state. | | `created_at` | string | Creation timestamp (ISO 8601). | | `updated_at` | string | Last update timestamp (ISO 8601). | | `is_first_activated` | bool | Whether the journey has been started at least once. | | `params` | [`JourneyParams`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#journeyparams) | Journey-wide configuration. | | `category_uuid` | string | UUID of the category, or empty if uncategorized. | | `pointCounts` | map | Count of points by type. | | `campaign_type` | [`CampaignType`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#campaigntype-enum) | How users enter the journey. | | `stop_reason` | string | Why the journey stopped, if applicable. | | `last_edited_by` | `User` | User who last edited the journey. | | `dynamic_entry` | bool | Whether dynamic entry is enabled. | ## JourneyParams Journey-wide configuration. Sent on create/update and returned inside `info.params`. | Field | Type | Description | | ----------------------------- | ---------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) the journey belongs to. Required on create. | | `silent_hours` | [`SilentHours`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#silenthours) | Hours during which messages are suppressed, per channel. | | `capping` | [`EntryCapping`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#entrycapping) | Limits on how often a user can re-enter the journey. | | `conversion_window` | [`ConversionWindow`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#conversionwindow) | Window for attributing goal conversions. | | `user_id_track_change_policy` | [`UserIDTrackChangePolicy`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#useridtrackchangepolicy-enum) | How to handle a user’s ID changing mid-journey. | ### SilentHours Suppresses sending during quiet hours. Configured **per channel**: each channel takes its own `SilentHoursParams`: | Field | Type | Description | | ----------------- | ------------------- | ------------------------------------ | | `push_params` | `SilentHoursParams` | Silent hours for push notifications. | | `inapp_params` | `SilentHoursParams` | Silent hours for in-app messages. | | `email_params` | `SilentHoursParams` | Silent hours for emails. | | `sms_params` | `SilentHoursParams` | Silent hours for SMS. | | `whatsapp_params` | `SilentHoursParams` | Silent hours for WhatsApp. | | `line_params` | `SilentHoursParams` | Silent hours for LINE. | Each `SilentHoursParams` is: | Field | Type | Description | | ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | bool | Whether silent hours apply to this channel. | | `from_time` | `Time` | Start of the quiet window: `{ "hour": 0–23, "minute": 0–59 }`. | | `to_time` | `Time` | End of the quiet window. | | `week_days` | bool[] | Seven booleans for the days the window applies (Monday = index 0). | | `behavior` | enum | What to do when a message falls inside silent hours: `WaitAndSend` (hold, then send when the window ends), `DropAndGo` (skip the message, continue the journey immediately), or `WaitAndDrop` (wait out the window, then continue without sending). | ### EntryCapping Limits how often the same user can enter the journey. | Field | Type | Description | | ------------ | ------ | --------------------------------------------------------------------------------------------------------- | | `is_enabled` | bool | Whether entry capping is on. | | `period` | uint64 | Minimum number of minutes between a user’s entries. `0` means the user can enter only once in a lifetime. | The limit is counted from the moment the user entered the journey, and it is tracked per user ID, so all devices of one user share a single entry. While the period lasts, the entry point rejects further entry attempts and counts them as errors instead of creating a traveler. Three behaviors to account for when you change these settings: * Leaving the journey early does not release the limit. The user waits out the full period even after reaching an exit point. * A changed `period` applies to entries made after the update. Users who entered earlier keep the period that was in effect when they entered. * Setting `is_enabled` to `false` lifts the limit for every user immediately. Note The Control Panel writes the same field from **Campaign entry limit**, which offers `0` (once in a lifetime), `1440` (once a day), `10080` (once a week), `43800` (once a month), and `131500` (once in three months). ### ConversionWindow | Field | Type | Description | | --------- | ------ | ---------------------------------------------------------------------------------------- | | `seconds` | uint64 | How long after entering a journey a user’s goal completion still counts as a conversion. | ## Point A point is a node on the journey canvas: an entry point, a message, a delay, a splitter, and so on. | Field | Type | Description | | ------------ | -------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | `uuid` | string | Unique ID of the point within the journey. Must be a canonical [RFC 4122](https://www.rfc-editor.org/rfc/rfc4122) UUID: 32 hex digits in 8-4-4-4-12 groups. | | `title` | string | Display name of the point. | | `point_type` | [`PointType`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#pointtype-enum) | The kind of node. | | `outputs` | array of [`PointOutput`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#pointoutput) | Connections to downstream points. | | `position` | [`Position`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#position) | Canvas coordinates. | | `point_data` | object | Exactly one nested key, matching `point_type` (see the [point types](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point-types-and-point_data) table). | Note All UUIDs (`info.uuid`, every point `uuid`, and each output’s `next_point_uuid`) must be canonical RFC 4122 UUIDs (8-4-4-4-12 hex groups). ### PointOutput A point’s outputs are its outgoing branches. Their keys are **not free-form**. The validator expects an exact set of keys for each point type, and rejects a journey whose point has the wrong number of outputs or a key it does not recognize. | Field | Type | Description | | ---------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `identity.key` | string | Branch key. Must follow the [output key rules](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#output-keys) below. | | `identity.order` | int | Display order of the branch. | | `info.title` | string | Optional branch label. | | `info.next_point_uuid` | string | UUID of the next point this branch connects to. Optional — leaving it unset ends the journey for the user, the same as an explicit [terminator](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#terminator) point. | #### Output keys The default (first) branch is always named `"default"`. Additional branches are named `"output1"`, `"output2"`, … (the prefix `output` followed by a 1-based index). Two point types break this rule, noted below. | Point type | Expected output keys | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | Entry points (`START_BY_SEGMENT`, `START_BY_API`, `EVENT`), `INAPP`, `SET_TAGS`, `WEBHOOK`, `AUDIENCE_SYNC`, and message points with no splitter (`SEND_PUSH`, `SEND_EMAIL`, `SEND_SMS`, `SEND_WHATSAPP`, `SEND_LINE`, `SEND_KAKAO`, `SEND_TELEGRAM`, `SEND_DATA`) | `default` | | `GOAL_EVENT`, `EXIT` | none (no outputs) | | `FILTER` | `default`, `output1` | | `BOOLEAN_SPLITTER` | `default`, then `output1` … `outputN` (one extra branch per condition. A simple yes/no split is `default` + `output1`) | | `WAIT` (delay) | `default`. A dynamic delay with branch splitting adds `output1` | | `WAIT_EVENT` | `default` is the **event-not-triggered** branch. `output1` (or, with a conditions script, one branch per condition) is the triggered path | | `SEND_PUSH` with a splitter | `default`, `output1` (and `output2` when both the message and delivery splitters are on) | | `SEND_EMAIL` / `SEND_SMS` / `SEND_LINE` / `SEND_WHATSAPP` with a splitter | `default`, `output1` | | `SEND_WHATSAPP` with a quick-reply preset | `default`, plus one branch per quick reply. The key is the quick-reply value itself | | `AB_SPLITTER` | `output0`, `output1`, `output2`, … (one per variant. **There is no `default` branch**) | ### Position | Field | Type | Description | | ----- | ----- | ------------------------------------ | | `x` | float | Horizontal coordinate on the canvas. | | `y` | float | Vertical coordinate on the canvas. | ## Point types and point_data `point_data` is a one-of: it carries exactly one nested object whose key is determined by the point’s `point_type`. | `point_type` | `point_data` key | Purpose | | ----------------------------- | ------------------ | --------------------------------------------------------------------------------------------------------------- | | `POINT_TYPE_START_BY_SEGMENT` | `start_by_segment` | Entry: users matching a segment. | | `POINT_TYPE_EVENT` | `message_bus` | Entry: users triggering an event. | | `POINT_TYPE_START_BY_API` | `start_by_api` | Entry: users injected via the [Start by API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) call. | | `POINT_TYPE_WAIT` | `delay` | Wait for a fixed or dynamic interval. | | `POINT_TYPE_WAIT_EVENT` | `wait_event` | Wait until an event happens. | | `POINT_TYPE_SEND_PUSH` | `send_push` | Send a push notification. | | `POINT_TYPE_SEND_EMAIL` | `send_email` | Send an email. | | `POINT_TYPE_SEND_SMS` | `send_sms` | Send an SMS. | | `POINT_TYPE_SEND_WHATSAPP` | `send_whatsapp` | Send a WhatsApp message. | | `POINT_TYPE_SEND_TELEGRAM` | `send_telegram` | Send a Telegram message. | | `POINT_TYPE_SEND_KAKAO` | `send_kakao` | Send a Kakao message. | | `POINT_TYPE_SEND_LINE` | `send_line` | Send a LINE message. | | `POINT_TYPE_SEND_DATA` | `send_data` | Send a silent data message. | | `POINT_TYPE_INAPP` | `inapp` | Show an in-app message. | | `POINT_TYPE_BOOLEAN_SPLITTER` | `boolean_splitter` | Split users by a condition (segment, tags, or event). | | `POINT_TYPE_AB_SPLITTER` | `ab_splitter` | Split users into A/B groups. | | `POINT_TYPE_FILTER` | `filter` | Allow only users matching a filter to continue. | | `POINT_TYPE_SET_TAGS` | `set_tags` | Update user tags. | | `POINT_TYPE_WEBHOOK` | `web_hook` | Send an outbound HTTP request. | | `POINT_TYPE_GOAL_EVENT` | `goal_event` | Track a conversion goal. | | `POINT_TYPE_AUDIENCE_SYNC` | `audience_sync` | Sync users to an external audience. | | `POINT_TYPE_EXIT` | `terminator` | Exit the journey. | Note The per-type `point_data` payloads are documented in the [Point reference](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/). Entry, timing, splitting, and action points are covered in full there. Messaging points are covered at the envelope level with links to the relevant channel docs. ### Example point A “set tags” point with a single downstream connection: ```json { "uuid": "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee", "title": "Tag as engaged", "point_type": "POINT_TYPE_SET_TAGS", "position": { "x": 480, "y": 120 }, "outputs": [ { "identity": { "key": "default", "order": 0 }, "info": { "title": "", "next_point_uuid": "ffffffff-1111-2222-3333-444444444444" } } ], "point_data": { "set_tags": { "application_code": "XXXXX-XXXXX", "tags": [ { "name": "engaged", "value_type": "TAG_VALUE_TYPE_BOOLEAN", "operation": "SET_TAG_OPERATION_SET", "bool": true } ] } } } ``` ## JourneyComment | Field | Type | Description | | ------------ | ----------------------- | ------------------------------- | | `id` | string | Comment UUID. | | `message` | string | Comment text. | | `position` | [`Position`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#position) | Canvas coordinates. | | `index` | int | Display order. | | `created_at` | string | Creation timestamp (ISO 8601). | | `deleted` | bool | Whether the comment is deleted. | ## Enums ### JourneyStatus enum `STATUS_DRAFT`, `STATUS_RUNNING`, `STATUS_FINISHED`, `STATUS_ARCHIVED`, `STATUS_PAUSED`, `STATUS_UNKNOWN`. ### CampaignType enum * `TriggerBased`: users enter on an event. * `AudienceBased`: users enter from a segment. * `APIBased`: users enter via the Start by API call. * `Mixed`: more than one entry type. * `Unknown`: entry type not determined. ### PointType enum See the [point types](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point-types-and-point_data) table above for the full list and the `point_data` key each maps to. ### UserIDTrackChangePolicy enum Controls what happens to a user who is mid-journey when their [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) changes: * `DEFAULT`: default behavior. * `TRACK`: keep tracking the user under the new ID. * `DROP`: remove the user from the journey when their ID changes. ## Related [Point reference ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/) [Create and update ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) --- # Lifecycle **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/ Start, pause, finish, or archive a Customer Journey by its UUID through the /api/v3/journeygateway endpoints. --- The lifecycle endpoints move a journey between states. Pass the journey’s [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) as `uuid` in the request body. The call returns the updated [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/). A journey is always in one of these states: | Status | Meaning | | ----------------- | ---------------------------------------------------------------------------------- | | `STATUS_DRAFT` | Being edited. Does not process users. | | `STATUS_RUNNING` | Active, processing users. | | `STATUS_PAUSED` | Temporarily halted, can be resumed. | | `STATUS_FINISHED` | Stopped, no longer processing users. Can be paused to resume editing, or archived. | | `STATUS_ARCHIVED` | Archived for storage. Terminal — cannot be reactivated through these endpoints. | ## Endpoints All lifecycle calls use the same request body: [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) passed as `uuid`. They differ only in the path: | Action | Method & path | Resulting state | | ------- | ------------------------------------- | ----------------- | | Start | `POST /api/v3/journeygateway/start` | `STATUS_RUNNING` | | Pause | `POST /api/v3/journeygateway/pause` | `STATUS_PAUSED` | | Finish | `POST /api/v3/journeygateway/finish` | `STATUS_FINISHED` | | Archive | `POST /api/v3/journeygateway/archive` | `STATUS_ARCHIVED` | ### Valid transitions | From | To | Via | | ----------------- | ----------------- | ------- | | `STATUS_DRAFT` | `STATUS_RUNNING` | Start | | `STATUS_RUNNING` | `STATUS_PAUSED` | Pause | | `STATUS_PAUSED` | `STATUS_RUNNING` | Start | | `STATUS_RUNNING` | `STATUS_FINISHED` | Finish | | `STATUS_PAUSED` | `STATUS_FINISHED` | Finish | | `STATUS_FINISHED` | `STATUS_PAUSED` | Pause | | `STATUS_FINISHED` | `STATUS_ARCHIVED` | Archive | Archiving is the only step in this table that cannot be reversed through the lifecycle endpoints. ## Request | Field | Required | Type | Description | | ------ | -------- | ------ | ------------------------------------------------------------------------------------------------ | | `uuid` | Yes | string | [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) of the journey to transition. | Body ```json { "uuid": "11111111-2222-3333-4444-555555555555" } ``` The API validates the current state before transitioning — see the table above. For example, starting an archived journey returns an error, since archiving has no reverse transition. Note To start a journey **and** apply pending edits in one call, use [Update and resume](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/#update-and-resume) instead of `start`. ### Request examples #### Start a journey ```bash curl -X POST https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/start \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "uuid": "11111111-2222-3333-4444-555555555555" }' ``` #### Pause a journey ```bash curl -X POST https://journey-api.svc-nue.pushwoosh.com/api/v3/journeygateway/pause \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "uuid": "11111111-2222-3333-4444-555555555555" }' ``` ## Response Returns the updated [journey object](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/): `info`, `points`, and `comments`. The `info.status` field reflects the new state. ### Response example ```json { "info": { "uuid": "11111111-2222-3333-4444-555555555555", "title": "Welcome series", "status": "STATUS_RUNNING", "created_at": "2026-05-01T09:00:00Z", "updated_at": "2026-06-17T12:00:00Z", "params": { "application_code": "XXXXX-XXXXX" }, "campaign_type": "TriggerBased" }, "points": [], "comments": [] } ``` See the [journey object reference](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) for the full field list. ## Related [Create and update ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) [Start by API ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) [Journey object ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) --- # Point reference **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/ Per-type point_data structures for Customer Journey points covering entry, timing, splitting, action, and messaging. --- Every [point](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point) carries a `point_data` object with exactly one nested key, matching the point’s `point_type` (see the [point-type → key mapping](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#point-types-and-point_data)). This page documents the structure under each key. Note Entry, timing, splitting, action, and conversion points are documented in full below. For messaging points (`send_*`, `inapp`), this page covers only the structural fields: which preset or content to reference and which channel settings to pass. The message content itself is defined in the preset and documented in the [Presets API](https://docs.pushwoosh.com/developer/api-reference/presets-api/) and per-channel docs. Some fields reference nested objects that mirror the controls in the Customer Journey builder. ## Entry points A journey needs at least one entry point. All entry points have a single `default` output. ### start_by_segment Enters users who match a segment. `POINT_TYPE_START_BY_SEGMENT`. | Field | Type | Description | | ------------------ | --------------- | ---------------------------------------------------------------------------------------------------------------- | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `filter_code` | string | [Segment/filter code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code) that defines the audience. | | `repeat` | `SegmentRepeat` | Optional recurring-entry schedule (see [SegmentRepeat](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#segmentrepeat)). | | `start_at` | timestamp | Do not process before this time (ISO 8601). | | `is_deactivated` | bool | Whether this entry point is turned off. | ### message_bus (event entry) Enters users when they trigger an event. `POINT_TYPE_EVENT`. | Field | Type | Description | | ------------------ | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `event_name` | string | [Event name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#event-name) to listen for. | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `event_conditions` | `PointEventCondition[]` | Conditions matched against the event’s attributes. | | `active_period` | `Period` | Time window during which the trigger is active. | | `multi_entry` | `MultiEntry` | How a user may enter more than once (see [MultiEntry](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#multientry)). | | `attribute_entry` | `AttributeEntry` | Enroll the user IDs carried in an event attribute instead of the user who triggered the event (see [AttributeEntry](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#attributeentry)). | | `allow_reenter` | bool | Whether users may re-enter the journey. | ##### AttributeEntry Enrolls the [User IDs](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) carried in an event attribute, instead of the user who triggered the event. Mirrors [Users from an event attribute](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#users-from-an-event-attribute) in the Customer Journey builder. | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------------------------------------------------- | | `enabled` | bool | Whether to enroll users named in an event attribute rather than the user who triggered the event. | | `attribute_name` | string | Name of the event attribute that holds the User ID (or list of User IDs) to enroll. | ### start_by_api Entry point fed by the [Start by API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) call. `POINT_TYPE_START_BY_API`. | Field | Type | Description | | ------------------ | --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `attributes` | string[] | Names of the custom attributes that can be passed in the `attribute_values` of a [Start by API](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/#request-body) request. | | `is_deactivated` | bool | Whether this entry point is turned off. | ## Timing points ### delay (wait) Pauses each user for a fixed or computed interval. `POINT_TYPE_WAIT`. The `point_data.delay` object sets exactly one of the following interval keys: | Interval key | Type | Description | | ---------------------- | -------------------------------- | -------------------------------------------------------------------------------------------------- | | `sec` | uint64 | Wait a fixed number of seconds. | | `exact_time` | [`ExactTime`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#time-objects) | Wait until a time of day (`hour`, `minute`). | | `exact_date_time` | [`ExactDateTime`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#time-objects) | Wait until an absolute date and time. | | `exact_week_days_time` | `ExactWeekDaysTime` | Wait until specific times on specific weekdays (map of day `0`=Monday … `6`=Sunday → `ExactTime`). | | `dynamic_time` | [`DynamicTime`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#dynamictime) | Wait relative to a value from a tag or event. | A `dynamic_time` with `split_branches: true` adds an `output1` branch (see [output keys](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#output-keys)). #### DynamicTime | Field | Type | Description | | --------------------------------------------- | --------------------------- | -------------------------------------------------------------------- | | `from` | enum `DynamicTimeFrom` | Whether the base time comes from a tag or an event. | | `direction` | enum `DynamicTimeDirection` | Offset before or after the base time. | | `duration` | uint64 | Size of the offset. | | `unit` | enum `DynamicTimeUnit` | Unit of `duration` (minutes, hours, days, …). | | `split_branches` | bool | Split users into separate branches when their computed times differ. | | `tag_name` | string | Tag holding the base time (when `from` is a tag). | | `event_name` / `event_attribute` / `point_id` | string | Event source of the base time (when `from` is an event). | ### wait_event Waits until users trigger an event (or a timeout). `POINT_TYPE_WAIT_EVENT`. The `default` output is the **not-triggered** path. The triggered path is `output1` or, with a script, one branch per condition. | Field | Type | Description | | ------------------- | ---------------------- | ------------------------------------------------------------------------------------------------- | | `events` | `PointWaitEventInfo[]` | Events to wait for. | | `conditions_script` | string | Optional script for complex multi-event logic. Its parsed condition names become the output keys. | | `multi_entry` | `MultiEntry` | Re-entry behavior. | ## Splitting and filtering points ### boolean_splitter Routes users down branches based on a condition. `POINT_TYPE_BOOLEAN_SPLITTER`. | Field | Type | Description | | -------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------- | | `split_strategy` | enum | `SPLIT_FILTER` (by segment), `SPLIT_TAGS` (by tag conditions), or `SPLIT_EVENTS` (by event conditions). | | `load_policy` | enum | `ALL` (any of the user’s devices may match) or `FILTER` (only the current device). | | `filter_code` | string | Segment code, when `split_strategy` is `SPLIT_FILTER`. | | `filter_expressions` | `FilterExpression[]` | Tag conditions, when `split_strategy` is `SPLIT_TAGS`: one extra `output` branch per expression. | | `event` | `BooleanSplitterEventInfo` | Event conditions, when `split_strategy` is `SPLIT_EVENTS`: one extra `output` branch per condition. | ### ab_splitter Randomly splits users into weighted variants. `POINT_TYPE_AB_SPLITTER`. Output keys are `output0`, `output1`, … (one per entry in `split`, indexed from 0. There is no `default`). | Field | Type | Description | | -------------------- | --------- | -------------------------------------------------------------------------------------- | | `split` | uint32[] | Percentage for each branch. Must sum to 100. Index `i` maps to output key `output{i}`. | | `auto_select_winner` | bool | Whether to automatically pick a winning branch. | | `winner_strategy` | enum | `ANY_GOAL` or `SPECIFIC_GOAL`. | | `goal_uuid` | string | Goal used to determine the winner (with `SPECIFIC_GOAL`). | | `winner_split` | uint32 | Winning branch number (1-based, `0` = no winner yet). | ### filter Lets only matching users continue (`default`), routing the rest to `output1`. `POINT_TYPE_FILTER`. | Field | Type | Description | | ------------ | ------------- | --------------------------------------------------------------------------------------------------------------------------------------- | | `filter` | enum `Filter` | A predefined check: `EMAIL_EXISTS`, `PUSH_TOKEN_EXISTS`, `SMS_TOKEN_EXISTS`, `WHATSAPP_TOKEN_EXISTS`, `LINE_TOKEN_EXISTS`, or `CUSTOM`. | | `filter_raw` | string | Custom filter expression, when `filter` is `CUSTOM`. | ## Action points ### set_tags Updates tags on the user. `POINT_TYPE_SET_TAGS`. | Field | Type | Description | | ------------------ | ---------------- | ------------------------------------------------------------------------------- | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `tags` | [`Tag`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#tag)[] | Tags to write. | #### Tag | Field | Type | Description | | --------------- | ---------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `name` | string | Tag name. | | `value_type` | enum `TagValueType` | Data type of the tag: `TAG_VALUE_TYPE_INTEGER`, `TAG_VALUE_TYPE_STRING`, `TAG_VALUE_TYPE_LIST`, `TAG_VALUE_TYPE_DATE`, `TAG_VALUE_TYPE_BOOLEAN`, `TAG_VALUE_TYPE_PRICE`, `TAG_VALUE_TYPE_VERSION`, `TAG_VALUE_TYPE_TIMEZONE`, `TAG_VALUE_TYPE_OBJECT`, or `TAG_VALUE_TYPE_OBJECT_LIST`. | | `operation` | enum `SetTagOperation` | What to do with the value: `SET_TAG_OPERATION_SET` (overwrite), `SET_TAG_OPERATION_APPEND` (add to a list), `SET_TAG_OPERATION_REMOVE` (remove from a list), `SET_TAG_OPERATION_INCREMENT` or `SET_TAG_OPERATION_DECREMENT` (change a number). | | `dynamic_value` | bool | Take the value at run time from an event or another tag instead of a fixed value. The source goes in the `dynamic` value key. | | *value* | oneof | Exactly one value key matching `value_type`: `str`, `int`, `bool`, `date`, `price`, `version`, `timezone`, or `list`. With `dynamic_value: true`, use `dynamic`. A `timezone` value is a UTC offset such as `UTC+01:00` or a number of seconds as a string, such as `"3600"`. A zone name such as `Europe/Berlin` isn’t accepted. | `operation` is required. A journey with a tag that has no `operation` fails validation with `Choose what Set tags does with the tag `. A request that still sends the removed `type` field is rejected with HTTP 400 and `unknown field "type"`. For example, this tag increases `visits` by 1: ```json { "name": "visits", "value_type": "TAG_VALUE_TYPE_INTEGER", "operation": "SET_TAG_OPERATION_INCREMENT", "int": 1 } ``` A field of an [object tag](https://docs.pushwoosh.com/developer/api-reference/object-tags/) (such as `order.amount`) is written like a regular tag of the field’s type. The Set tags point can’t write a whole object (`TAG_VALUE_TYPE_OBJECT`, `TAG_VALUE_TYPE_OBJECT_LIST`) or a field of a list of objects. A journey with such a tag fails validation. ### web_hook Sends an outbound HTTP request. `POINT_TYPE_WEBHOOK`. Single `default` output. | Field | Type | Description | | ------------------ | -------------------- | ------------------------------------------------------------------------------------------------------------------ | | `method` | string | HTTP method (`GET`, `POST`, `PUT`, …). | | `url` | string | Target URL. | | `request_template` | string | Request body template. Supports [Liquid](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) templating. | | `headers` | map | HTTP headers. | | `disabled` | bool | Read-only. `true` when Pushwoosh auto-disabled the webhook after repeated errors. | | `reactivate` | bool | Write-only. Set `true` to re-enable a disabled webhook. | ### audience_sync Adds or removes the user from a Meta Custom Audience. `POINT_TYPE_AUDIENCE_SYNC`. | Field | Type | Description | | ----------------- | ------------------------- | ------------------------------------------------------------ | | `ad_account_id` | string | Meta ad account ID. | | `ad_account_name` | string | Display name for `ad_account_id`, for UI only. | | `audience_id` | string | Meta Custom Audience ID. | | `audience_name` | string | Display name for `audience_id`, for UI only. | | `action` | enum `AudienceSyncAction` | `AUDIENCE_SYNC_ACTION_ADD` or `AUDIENCE_SYNC_ACTION_REMOVE`. | Caution This isn’t checked when you create or update the point — only when the journey is validated or [started](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/#endpoints). A bad `ad_account_id` (no longer connected) or `audience_id` (not returned by Meta) saves without error and only surfaces at that point, not silently on each user’s sync. ## Conversion points ### goal_event Tracks a conversion goal. `POINT_TYPE_GOAL_EVENT`. Has no outputs. | Field | Type | Description | | ------------------ | ----------------------- | ------------------------------------------------------------------------------- | | `event_name` | string | Goal event name. | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `event_conditions` | `PointEventCondition[]` | Conditions matched against the goal event’s attributes. | | `is_throw_out` | bool | Whether to remove the user from the journey once the goal is reached. | | `is_cancel_event` | bool | Whether this is a negative (cancellation) goal. | ## Exit points ### terminator Ends the journey for the user. `POINT_TYPE_EXIT`. Empty object (`{}`) and no outputs. ```json { "point_type": "POINT_TYPE_EXIT", "point_data": { "terminator": {} }, "outputs": [] } ``` A terminator point is optional. Leaving an output’s `info.next_point_uuid` unset ends the journey for the user the same way an explicit terminator does — both cases are counted the same way, as **Exits**, not as errors. ## Messaging points All messaging points send through a channel and share a common envelope. Each has a single `default` output unless a splitter is enabled (see [output keys](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/#output-keys)). Common fields: | Field | Type | Description | | ------------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `application_code` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `preset_code` | string | [Preset](https://docs.pushwoosh.com/developer/api-reference/presets-api/) (template) to send. `inapp` uses `richmedia_code` instead. | | `content` / `title` | string | Inline message content, when not using a preset (channel-dependent). | | `campaign_code` | string | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code), assigned when the journey starts. **Create and new points:** leave empty, except for the system campaign created for this point. **Existing points:** leave empty or as read. Otherwise the request is rejected. | | `split_params` | [`BehaviorSplitter`](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#behaviorsplitter) | Optional: wait for an interaction and branch on it. | | `message_type` | enum | [Marketing vs transactional](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) message type (controls control-group handling). Not used by `send_email`, which has its own `email_type`/`email_category` pair below. | Point types and their notable extra fields: | Point type | `point_data` key | Notes | | -------------------------- | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `POINT_TYPE_SEND_PUSH` | `send_push` | `split_params` (behavior) plus a delivery splitter can produce up to three outputs. `send_by_user_id`, `wait_optimal_time`. | | `POINT_TYPE_SEND_EMAIL` | `send_email` | `liquid_template_params`, `personalise`, `email_type`, `email_category` (see below). See the [Email API](https://docs.pushwoosh.com/developer/api-reference/email-api/). | | `POINT_TYPE_SEND_SMS` | `send_sms` | See [SMS](https://docs.pushwoosh.com/developer/api-reference/sms/). | | `POINT_TYPE_SEND_WHATSAPP` | `send_whatsapp` | `personalise` (see below). A quick-reply preset adds one output per reply (key = reply value). See [WhatsApp](https://docs.pushwoosh.com/developer/api-reference/whatsapp/). | | `POINT_TYPE_SEND_TELEGRAM` | `send_telegram` | Telegram channel. | | `POINT_TYPE_SEND_KAKAO` | `send_kakao` | See [Kakao](https://docs.pushwoosh.com/developer/api-reference/kakao/). | | `POINT_TYPE_SEND_LINE` | `send_line` | See [LINE API](https://docs.pushwoosh.com/developer/api-reference/line-api/). | | `POINT_TYPE_SEND_DATA` | `send_data` | Silent data message (no UI). | | `POINT_TYPE_INAPP` | `inapp` | Uses `richmedia_code`, `expire_in_days`, `capping`. | Caution On `send_email`, `email_type` must be omitted or set to exactly `marketing` or `transactional` — lowercase, any other value is rejected. `email_category` is required when `email_type` is `marketing`, and must be an existing category’s **code**, not its display name. On `send_whatsapp`, a `personalise.placeholders[]` entry with `type: "MPM"` (multi-product message) must have `actions.actions[].type` set to exactly `action` — the only action type Meta accepts in an MPM template button. Any other value is rejected. None of this is checked when you create or update the point — only when the journey is validated or [started](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/#endpoints). A bad value saves without error and only surfaces at that point. ## Shared types ### SegmentRepeat | Field | Type | Description | | ----------- | ------------------- | -------------------------------------------- | | `enabled` | bool | Whether recurring entry is on. | | `frequency` | string | `daily`, `weekly`, `monthly`, or `dates`. | | `every` | uint32 | Repeat every N periods. | | `dates` | string[] | Specific dates, when `frequency` is `dates`. | | `time` | `SegmentRepeatTime` | Time-of-day configuration. | ### Time objects * `ExactTime`: `{ "hour": 0–23, "minute": 0–59 }`. * `ExactDateTime`: `{ "year", "month": 1–12, "day": 1–31, "hour": 0–23, "minute": 0–59 }`. ### BehaviorSplitter | Field | Type | Description | | ---------- | ------ | ---------------------------------------------------- | | `enabled` | bool | Whether to wait for an interaction and branch on it. | | `interval` | uint64 | Seconds to wait for the interaction. | ### MultiEntry Controls how many active sessions a user can have in the journey at the same time. Mirrors [Control how many sessions a user can have at the same time](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-how-many-sessions-a-user-can-have-at-the-same-time) in the Customer Journey builder. Used by the [event entry point](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#message_bus-event-entry) and [wait_event](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#wait_event). | Field | Type | Description | | ---------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `enabled` | bool | Whether a user can have multiple active sessions at the same time. `false`: one active session per user. `true`: multiple active sessions per user. | | `attribute_name` | string | Optional. Event attribute that scopes each session. When set, a user can have one active session per distinct value of this attribute (for example, one per `order_id`). Events with the same value share one session. Leave empty to scope all of the user’s entries together. | ## Related [Journey object ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/journey-object/) [Create and update ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/) [Presets API ](https://docs.pushwoosh.com/developer/api-reference/presets-api/) --- # Start by API **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/ Inject users into the API entry point of a running Customer Journey using /api/journey/{id}/start/external. --- `POST` `https://journey.pushwoosh.com/api/journey/{id}/start/external` Enters a set of users into a journey’s **API entry point**. Use it to drive journeys from your own backend. For example, start an onboarding flow when a user completes signup on your server. Tip Do not confuse this call with [Lifecycle start](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/#endpoints), which activates a journey and moves it into **Running**. Start by API injects users into a journey that is already running. See the [comparison table](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/#lifecycle-start-vs-start-by-api). ## Prerequisites * The journey is in the **Running** state. * The journey contains exactly one API entry point (the “Start by API” element), and that element is not deactivated. * The attribute names you send match the attributes configured on that API entry point. Rate limit Each API entry point accepts **one request per minute**. A second request within that window returns an error (`Enhance your calm! only one request per minute is allowed`). Batch your recipients into a single request rather than sending many small ones. ## Path parameters | Name | Type | Description | | ---- | ------ | ------------------------------------------------------------------------------------------ | | `id` | string | [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) of the running journey. | ## Request headers | Name | Required | Value | | --------------- | -------- | -------------------------------------------------------------------------------------------------------------- | | `Content-Type` | Yes | `application/json` | | `Authorization` | Yes | `Api `. See [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). | ## Request body The body has a single `payload` object. You must provide **exactly one** of `users`, `hwids`, or `filter` to select who enters the journey. | Field | Type | Description | | -------------------------- | -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `payload.users` | string[] | [User IDs](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to enter. Mutually exclusive with `hwids` and `filter`. | | `payload.hwids` | string[] | [HWIDs](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid) to enter. Mutually exclusive with `users` and `filter`. | | `payload.filter` | string | A [seglang](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) expression selecting the audience. Mutually exclusive with `users` and `hwids`. | | `payload.attribute_values` | map | Optional. Values for the custom attributes defined on the API entry point. Each key must match a configured attribute name. | ### Request examples ##### Enter specific users ```bash curl -X POST 'https://journey.pushwoosh.com/api/journey//start/external' \ -H 'Authorization: Api YOUR_API_TOKEN' \ -H 'Content-Type: application/json' \ -d '{ "payload": { "users": ["user-123", "user-456"] } }' ``` ##### Enter specific users with attributes ```json { "payload": { "users": ["user-123", "user-456"], "attribute_values": { "promo_code": "SUMMER25", "tier": "gold" } } } ``` ##### Enter an audience by filter ```json { "payload": { "filter": "A(\"XXXXX-XXXXX\").tags(\"City\").eq(\"London\")" } } ``` ## Response * 200 ```json { "request_uuid": "9f8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d" } ``` | Field | Type | Description | | -------------- | ------ | ---------------------------------------------------------------------------------- | | `request_uuid` | string | Identifier of the accepted entry request. The request is processed asynchronously. | * 400 Validation errors return HTTP `400` with a descriptive message. Common cases: | Message | Cause | | ----------------------------------------------------------- | ------------------------------------------------------------------- | | `one of users, hwids or filter must be provided` | None of the three selectors was set. | | `only one of users, hwids or filter must be provided` | More than one selector was set. | | `Journey is not running` | The journey is not in the Running state. | | `zero api start points` | The journey has no API entry point. | | `there is more then one api start point` | The journey has more than one API entry point. | | `point is deactivated` | The API entry point is deactivated. | | `unknown attribute: ` | An `attribute_values` key is not configured on the API entry point. | | `Enhance your calm! only one request per minute is allowed` | Rate limit hit (one request per minute per entry point). | ## Related [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) [Get Journey stats ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) [Segmentation language ](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) --- # Get Journey stats **URL:** https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/ Retrieve per-point delivery and conversion statistics for a Customer Journey by its ID. --- `GET` `https://journey.pushwoosh.com/api/journey/{id}/statistics/external` Returns statistics for a Customer Journey by its ID. Replace `{id}` in the URL with the [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) from the journey canvas page URL: ![Journey ID in the Journey canvas URL](https://docs.pushwoosh.com/api-reference-customer-journey-statistics-1.webp) Send a GET request with no body. #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------------------ | -------------------------------------------------------------------------------- | | Authorization | Yes | `Api ` | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). | #### Request example ```shell curl --location --request GET 'https://journey.pushwoosh.com/api/journey//statistics/external' \ --header 'Authorization: Api ' ``` #### Response example * 200 ```json { "payload": { "title": "Journey Title", "points": [{ "pointTitle": "First Point Title", "campaignCode": "AAAAA-00000", "presetCode": "12345-67890", "pointStat": { "sent": 22648, "opened": 60, "conversion": 0.26492405510420347 } }] } } ``` #### In-app `pointStat` fields The fields inside `pointStat` depend on the journey element’s channel. In-app elements return the metrics below instead of the `sent`, `opened`, and `conversion` fields shown in the response example above. | Field | Type | Description | | -------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `shows` | number | Number of times in-app messages were displayed to users. Labeled **Impressions** in [Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#metrics-by-channel). | | `interactions` | number | Number of user interactions with the in-app message (for example, button clicks). Labeled **Interactions** in [Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#metrics-by-channel). | | `skipped` | number | Number of times users skipped or dismissed the in-app message without interacting. Labeled **In-App Skips** in [Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#metrics-by-channel). | #### In-app response example ```json { "payload": { "title": "Journey Title", "points": [{ "pointTitle": "In-App element title", "campaignCode": "AAAAA-00000", "presetCode": "12345-67890", "pointStat": { "shows": 3148, "interactions": 2759, "skipped": 36 } }] } } ``` ## Related [Remove users from journeys ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/) [Lifecycle ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/lifecycle/) [Start by API ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/start-by-api/) --- # Data Erasure API **URL:** https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/ Create a self-serve GDPR data-subject erasure task, upload a subject list by CSV, and download the report through a REST API. --- The Data Erasure API is the self-serve way to erase a subject’s personal data from Pushwoosh (GDPR right to erasure). Erasure is irreversible: use it to mirror the Control Panel’s [Data requests](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) page and the [Erase personal data](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#erase-personal-data) action on a subscriber’s card from your own systems. ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json`, except [Upload](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#upload), which takes `multipart/form-data`. ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` The token must have modify access to every application named in `application_codes`. That’s the same access a token needs to write device data on those applications. ## Conventions * **Field naming:** request bodies accept `lowerCamelCase` (for example, `applicationCodes`, `identifierType`). Responses are marshaled using the proto field names, in `snake_case` (`task_id`, `identifier_hash`, and so on). The examples and object references below use that casing. * **Identifiers:** a subject is addressed by `identifier_type` (`SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`) plus a list of `identifiers` of that one type. An email identifier is matched case-insensitively. * **Dry run first, then quote it:** [Create](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#create) with `dry_run: true` only counts what would be erased and writes nothing. Quoting that dry run’s `task_id` as `confirmation_dry_run_task_id` on the real call is optional, but if you quote one, it must match that dry run’s own applications and identifiers exactly, and be called within 15 minutes of it finishing. Otherwise the call is refused. * **Reports never carry the identifier in the clear.** Both [GetTaskReport](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#gettaskreport) and [GetJournalReport](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#getjournalreport) report each identifier as its sha256 hash (`identifier_hash`), and the report body is CSV encoded as base64 inside the JSON response. ### Error responses | HTTP status | Meaning | | :---------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `400 Bad Request` | Invalid argument, such as an empty `identifier_type`, `identifiers`, or `application_codes`, more than 10,000 identifiers, more than 50 applications, identifiers × applications over 50,000, an empty identifier string, or (Upload only) a missing/unreadable CSV file. Also returned as `FailedPrecondition` on the wire when a quoted `confirmation_dry_run_task_id` doesn’t match the finished dry run it names, is too old (over 15 minutes), or isn’t a finished dry run at all. | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The token has no modify/device-data right on one of the requested applications, or the caller may not read the requested task/report. | | `404 Not Found` | The task was not found, or doesn’t belong to the caller’s account. | | `413 Payload Too Large` | (Upload only) the uploaded file is over 5 MB. | | `429 Too Many Requests` | The account already started 20 data erasure tasks (dry runs included) in the last 24 hours. | ## Endpoints | Method | Path | Description | | :----- | :----------------------------------------- | :--------------------------------------------------------- | | `POST` | `/api/data_erasure/tasks` | Create an erasure (or dry-run) task | | `POST` | `/api/data_erasure/upload` | Create an erasure task from an uploaded CSV of identifiers | | `GET` | `/api/data_erasure/tasks` | List the account’s erasure tasks | | `GET` | `/api/data_erasure/tasks/{task_id}` | Get one erasure task | | `GET` | `/api/data_erasure/tasks/{task_id}/rows` | List one task’s per-identifier results | | `GET` | `/api/data_erasure/tasks/{task_id}/report` | Download one task’s report as CSV | | `GET` | `/api/data_erasure/report` | Download every task of a period as one CSV | ## Create Starts erasing the personal data of the listed subjects in the listed applications. With `dry_run: true` the task only counts what would be erased. `POST` `/api/data_erasure/tasks` ### Request body | Parameter | Type | Required | Description | | :------------------------- | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `identifierType` | string | Yes | `SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`. | | `identifiers` | array of strings | Yes | Subject identifiers of that one type, up to 10,000 per task, no empty values. | | `applicationCodes` | array of strings | Yes | [Application codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) to erase from, up to 50. There’s no way to erase from every application of the account in one call, so name each one explicitly. `identifiers.length × applicationCodes.length` must not exceed 50,000, because the report holds one row per identifier per application. | | `dryRun` | boolean | No | Counts what would be erased and writes nothing. Defaults to `false`. | | `confirmationDryRunTaskId` | integer | No | `task_id` of a finished dry run that measured this exact erase. See [Conventions](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#conventions). | ##### Request example ```json { "identifierType": "SUBJECT_IDENTIFIER_EMAIL", "identifiers": ["subject@example.com"], "applicationCodes": ["XXXXX-XXXXX"], "dryRun": true } ``` ### Response | Field | Type | Description | | :-------- | :------ | :--------------------------------------------------------------------- | | `task_id` | integer | Id of the created task. Use it with every other endpoint on this page. | Danger Erasure cannot be undone. Erasing a subject removes their data in every named application, but does not cancel messages already scheduled or queued for that subject, for example a delayed send or a running journey’s next step. ## Upload Creates the same kind of task as [Create](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#create), but the identifiers come from an uploaded CSV instead of a JSON array. Use this for a bulk subject list. `POST` `/api/data_erasure/upload` ### Request (multipart/form-data) | Field | Required | Description | | :----------------------------- | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `file` | Yes | CSV file, up to 5 MB, one identifier per row, in the single column. The first row may name the column (`user_id`, `userid`, `user id`, `hwid`, `email`, `identifier`, or `subject`, case-insensitively) instead of holding an identifier. That row is then skipped as a header, not counted. Blank rows are skipped and counted separately. Duplicate identifiers (case-insensitively, for email) are collapsed to one and counted separately. | | `identifier_type` | Yes | `user_id`, `hwid`, or `email`, lowercase, a different casing from the JSON `identifierType` enum values used elsewhere on this page. One type applies to the whole file. | | `application_codes` | Yes | Comma-separated application codes. | | `dry_run` | No | `true`/`false`. | | `confirmation_dry_run_task_id` | No | Same as `Create`. | ### Response ```json { "task_id": 123, "identifier_count": 480, "duplicate_count": 3, "skipped_rows": 1 } ``` ## ListTasks Lists the erasures of the account, newest first: the audit journal of who erased what and when. Dry runs are left out unless `includeDryRuns` is set. `GET` `/api/data_erasure/tasks` ### Query parameters | Parameter | Type | Description | | :----------------- | :---------------- | :----------------------------------------------------------------------------------- | | `limit` / `offset` | integer | Paging. | | `includeDryRuns` | boolean | Include dry-run (measurement-only) tasks. | | `from` / `to` | string (RFC 3339) | Keep tasks started in `[from, to)`. | | `applicationCode` | string | Keep tasks that cover this application. | | `initiatorUserId` | integer | Keep tasks started by this Control Panel user. `0` (default) keeps every initiator. | | `initiatorTokenId` | integer | Keep tasks started by this API token. `0` (default) keeps every initiator. | | `initiatorEmail` | string | Keep tasks started by this Control Panel user, matched by email, case-insensitively. | ### Response | Field | Type | Description | | :------ | :-------------------------------------------------------------- | :-------------- | | `tasks` | array of [Data erasure task objects](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#data-erasure-task-object) | Matching tasks. | ## GetTask Returns one erasure task with its status and counters. Use it to follow a task started by [Create](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#create) or [Upload](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#upload). `GET` `/api/data_erasure/tasks/{task_id}` ### Response | Field | Type | Description | | :----- | :---------------------------------------------------- | :------------------ | | `task` | [Data erasure task object](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#data-erasure-task-object) | The requested task. | ## ListTaskRows Returns the per-identifier outcome of one task: what was deleted, not found, skipped, or failed. Identifiers are reported as their sha256 hash. `GET` `/api/data_erasure/tasks/{task_id}/rows` ### Query parameters | Parameter | Type | Description | | :----------------- | :------ | :---------- | | `limit` / `offset` | integer | Paging. | ### Response | Field | Type | Description | | :----- | :---------------------------------------------------------------------- | :-------------------- | | `rows` | array of [Data erasure task row objects](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#data-erasure-task-row-object) | This page of results. | ## GetTaskReport Returns the whole report of one task as a CSV file: one row per identifier per application, with its outcome. [ListTaskRows](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#listtaskrows) shows the same rows page by page instead. `GET` `/api/data_erasure/tasks/{task_id}/report` ### Response | Field | Type | Description | | :------------- | :-------------- | :------------------------------------------------------------------------------------------------------------------------- | | `filename` | string | Suggested file name, for example `data-erasure-task-42.csv`. | | `content_type` | string | `text/csv`. | | `content` | string (base64) | The CSV file. Columns: `task_id`, `created`, `identifier_type`, `identifier_hash`, `application_code`, `result`, `detail`. | ## GetJournalReport Returns every erasure of a period as one CSV file: a row per identifier per application, with the task and its initiator on each row. This is what an account hands to an auditor. `GET` `/api/data_erasure/report` ### Query parameters | Parameter | Type | Required | Description | | :--------------- | :---------------- | :------- | :---------------------------------- | | `from` | string (RFC 3339) | Yes | Start of the period. | | `to` | string (RFC 3339) | No | End of the period. Defaults to now. | | `includeDryRuns` | boolean | No | Include dry-run tasks. | Caution A period with more than 2,000 erasure tasks is refused instead of being cut short. Narrow the period and call again. ### Response | Field | Type | Description | | :------------- | :-------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `filename` | string | Suggested file name, for example `data-erasure-2026-01-01-2026-04-01.csv`. | | `content_type` | string | `text/csv`. | | `content` | string (base64) | The CSV file. Columns: `task_id`, `task_created`, `initiator_user_id`, `initiator_email`, `initiator_token_id`, `dry_run`, `identifier_type`, `identifier_hash`, `application_code`, `result`, `detail`, `row_created`. | ## Data erasure task object | Field | Type | Description | | :------------------- | :---------------- | :-------------------------------------------------------------------------------------- | | `task_id` | integer | Task id. | | `identifier_type` | string | `SUBJECT_IDENTIFIER_USER_ID`, `SUBJECT_IDENTIFIER_HWID`, or `SUBJECT_IDENTIFIER_EMAIL`. | | `application_codes` | array of strings | Applications the task covers. | | `dry_run` | boolean | Whether this task only measured a volume. | | `status` | string | `ERASURE_TASK_STATUS_PENDING`, `_IN_PROGRESS`, `_DONE`, or `_FAILED`. | | `total_count` | integer | Identifiers × applications this task covers. | | `deleted_count` | integer | How many were erased (or, for a dry run, would be). | | `not_found_count` | integer | How many matched nothing. | | `failed_count` | integer | How many failed. See [ListTaskRows](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#listtaskrows) or the report for why. | | `fail_reason` | string | Set when `status` is `ERASURE_TASK_STATUS_FAILED`. | | `initiator_user_id` | integer | Control Panel user who started the task. `0` when an API token did. | | `initiator_token_id` | integer | API token that started the task. `0` when a Control Panel user did. | | `initiator_email` | string | Address of the Control Panel user who started the task. Empty for an API token. | | `created` | string (RFC 3339) | When the task was created. | | `started_at` | string (RFC 3339) | When erasure work began. | | `finished_at` | string (RFC 3339) | When the task reached a final status. | ## Data erasure task row object | Field | Type | Description | | :----------------- | :---------------- | :---------------------------------------------------------------------------------- | | `identifier_hash` | string | sha256 of the identifier. The report never stores or returns the identifier itself. | | `application_code` | string | Application this row’s outcome applies to. | | `result` | string | `ERASURE_ROW_RESULT_DELETED`, `_NOT_FOUND`, `_SKIPPED`, or `_FAILED`. | | `detail` | string | Technical detail, mainly set on failure. | | `created` | string (RFC 3339) | When this row was written. | ## Related [Data requests (product guide) ](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) [Erase personal data from User Explorer ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#erase-personal-data/) --- # Device API **URL:** https://docs.pushwoosh.com/developer/api-reference/device-api/ Register devices, manage tags, track events, and handle device-related operations using Device API methods. --- The Device API registers devices in a Pushwoosh application and records their activity: tag values, app opens, push opens, and push deliveries. The Pushwoosh SDK calls most of these methods automatically, so call them directly when you manage devices from your own server or a custom client. To register or tag many devices in one request, use the [Audience API](https://docs.pushwoosh.com/developer/api-reference/audience-api/). ## registerDevice `POST` `https://api.pushwoosh.com/json/1.3/registerDevice` Called internally from the SDK. Registers device for the application. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | push_token | string | [Push token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token) for the device. | | language | string | Language locale of the device. Must be a lowercase two-letter code according to ISO-639-1 standard. | | hwid* | string | Unique string to identify the device (IDFV on iOS, randomly generated value on Android). [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) | | timezone | integer | Timezone offset in seconds for the device. | | device_type* | integer | [Device type](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type). See possible values below. | | email | string | Email address to register (use for email users instead of HWID and push token). | | tags | object | Tag values to assign to the device registered. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "push_token": "dec301908b9ba8XXXXX57a58e40f96f5XXXXX2068674f5XXXXa25cdc250a2a41", // optional. "hwid": "1CA6XXXXX-8DAC-XXXXX-XXXXX-B756288B6D3C", // required. Hardware device ID "idfa": "AEBE52E7-0XXXXX-455A-XXXXX-E57283966239", // optional. "timezone": 3600, // optional. Offset in seconds "device_type": 1, // required. See the possible values below. For emails, // use the "emails" params as described below. "email": "email_address@domain.com", // use instead of "hwid" and "push_token" to register // the email address for your email project "language": "en", // optional. ISO 639-1|639-2 language code "userId": "Alex", // optional. "tags": { // optional. Tag values to set for the device registered "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], // sets the list of values for Tags of List type "DateTag": "2024-10-02 22:11", // note the time should be in UTC "BooleanTag": true // valid values are: true, false }, // system tags, optionals "app_version": "1.2.3", "device_model": "Samsung SM-G355H", "os_version": "2.3", // optional encryption keys for chrome/firefox "public_key": "BNmDO4BTKEMJqaqprTf7t/HBXXXXX/orcXXXXX/scS5CFP6XXXXXHI1/GgRQD8c4kTxTEEF0quvIUiLQqoBY0/Qo=", "auth_token": "RlRmCXXXXX/s7XXXXXjKFzoQ==", // optional FCM keys for Chrome (for XMPP) "fcm_token": "BNmDO4BTKEMJXXXXXprTf7t/XXXXXBQ/orXXXXXc/scS5CFP6zhQGIHI1/GgRQD8c4kTxTEEF0quvIUiLQqoBY0/Qo=", "fcm_push_set": "RlXXXXXGM/s7XXXXXjKFzoQ==" } } ``` Possible device types: * 1 – iOS * 3 – Android * 7 – Mac OS X * 8 – Windows * 9 – Amazon * 10 – Safari * 11 – Chrome * 12 – Firefox * 13 – Internet Explorer * 14 – Email * 17 – Huawei * 18 – SMS * 20 – Web * 21 – WhatsApp * 22 – Line * 23 – Kakao Talk * 24 – Telegram * 25 – Apple Wallet * 26 – Google Wallet * 27 – Viber ### Registering email devices To **register a email subscriber** for your app, send the `"email": "email_address@domain.com"` param in your `/registerDevice` or [`/registerEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api#registeremail) request as follows: ##### Request example ```json { "request":{ "application": "XXXXX-XXXXX", // required. Pushwoosh application code "email": "email_address@domain.com", // required. Email address to register for your email project "language": "en", // optional. ISO 639-1|639-2 language code "userId": "Alex", // optional. "tags": { // optional. Tag values to set for the device registered "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], // sets the list of values for Tags of List type "DateTag": "2024-10-02 22:11", // note the time should be in UTC "BooleanTag": true // valid values are: true, false } } } ``` ### Registering WhatsApp devices To register a WhatsApp device for your app, follow these guidelines: * **hwid**: Ensure this field includes the whatsapp: prefix followed by the phone number in E.164 format (e.g., whatsapp:+0000000000). The phone number must be valid, which Pushwoosh will verify. * **Push token**: A push token is not required, as hwid will automatically function as the push token. * **device_type**: Set this field to 21 to specify WhatsApp as the platform. ##### Request example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "whatsapp:+0000000000", // required. WhatsApp prefix and valid phone number "timezone": 3600, // optional. Time offset in seconds "device_type": 21, // required. WhatsApp device type is 21 "language": "en", // optional. ISO 639-1|639-2 language code "userId": "Alex", // optional. User identifier "tags": { // optional. Tag values for custom segmentation "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], "DateTag": "2024-10-02 22:11", // UTC format "BooleanTag": true }, "app_version": "1.2.3", // optional. Application version "device_model": "Samsung SM-G355H", // optional. Device model "os_version": "2.3" // optional. Operating system version } } ``` ### Registering SMS devices To register an SMS device for your app, follow these guidelines: * **hwid**: Ensure this field includes the phone number in E.164 format (e.g., +0000000000). The phone number must be valid, which Pushwoosh will verify. * **Push token**: A push token is not required, as hwid will automatically function as the push token. * **device_type**: Set this required field to 18 to designate SMS as the platform. ##### Request example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "+0000000000", // required. Valid phone number in E.164 format "timezone": 3600, // optional. Time offset in seconds "device_type": 18, // required. SMS device type is 18 "language": "en", // optional. ISO 639-1|639-2 language code "userId": "Alex", // optional. User identifier "tags": { // optional. Tag values for custom segmentation "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], "DateTag": "2024-10-02 22:11", // UTC format "BooleanTag": true }, "app_version": "1.2.3", // optional. Application version "device_model": "Samsung SM-G355H", // optional. Device model "os_version": "2.3" // optional. Operating system version } } ``` **Status codes**: | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Device successfully registered | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | ## unregisterDevice `POST` `https://api.pushwoosh.com/json/1.3/unregisterDevice` Removes the device’s push token. The unregistered device is still counted in Total Devices and can be reached with In-Apps. Called internally from the SDK. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in /registerDevice request. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16XXXXXe7a6beceXXXXX530fb2" // required. Hardware device ID used in /registerDevice API } } ``` Note For emails call [`/deleteEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api/#deleteemail). **Status codes**: | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Device successfully unsubscribed | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | ## deleteDevice `POST` `https://api.pushwoosh.com/api/v2/device-api/deleteDevice` Deletes a device and all its associated data identified by the specified [HWID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) within the application. Unlike [`/unregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#unregisterdevice), which only removes the push token and keeps the device record, `/deleteDevice` fully removes the device. The request is processed asynchronously, and the endpoint returns `200 OK` as soon as the delete request has been accepted for processing. #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | | Content-Type | Yes | application/json | | #### Request body | Name | Required | Type | Description | | ----------- | -------- | ------ | ---------------------------------------------------------------------------------------------------- | | application | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid | Yes | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) of the device to delete. | #### Request example ```json { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16df378e7a6bece9614e1530fb2" // required. Hardware device ID of the device to delete } ``` #### Response example * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` ##### Status codes | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Delete request accepted | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 401 | N/A | Missing or invalid Authorization token | | 500 | 500 | Internal error | ## setTags `POST` `https://api.pushwoosh.com/json/1.3/setTags` Sets tags values for the device. Called from the SDK. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in /registerDevice request. | | tags* | object | JSON object of tags to set, send “null” to remove the value. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Note The method is called from the SDK. It is possible to call it remotely from your backend, however you need to maintain an up-to-date database of hwid’s on the backend side. A tag name that doesn’t exist yet is created automatically, with the type inferred from the value: a whole number becomes Integer, a number with a decimal point becomes Price, a string becomes String (or Date, for a recognized date-time format such as `2024-10-02 22:11`), an array becomes List, and a boolean becomes Boolean. An auto-created tag is always User-specific. Create a Version tag, which auto-creation can’t infer, or a General tag explicitly first, via the Control Panel or [`/addTag`](https://docs.pushwoosh.com/developer/api-reference/tags#addtag). Tag auto-creation is enabled by default for the vast majority of accounts. If `setTags` doesn’t create a new tag as expected, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to check whether it’s been turned off for your account. Example ```json { "request":{ "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16XXXXXe7a6becXXXXXe1530fb2", // required. Hardware device ID used in /registerDevice API "tags": { // required. "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], // sets the list of values for Tags of List type "DateTag": "2024-10-02 22:11", // note the time is in UTC "BooleanTag": true // valid values are - true, false } } } ``` ### Increment Integer tag values To **increment** a value of the Integer Tag, use the `operation` parameter with the “increment” value as follows: ```json { "request":{ "application": "12345-67890", // required. Pushwoosh application code "hwid": "21AB7628-XXXX-XXXX-CCC0-PO287CS24CA4", // required. hardware device ID used in /registerDevice API "tags": { // required. "Level": { // Tag name "operation": "increment", // overwrites the integer tag in increments of the following value "value": 1 // increment for the tag value } } } } ``` ### Decrement Integer tag values To **decrement**, use the negative numbers as the value for the “increment” operation (-1, -2, -3,-n): ```json { "request":{ "application": "12345-67890", // required. Pushwoosh application code "hwid": "21AB7628-XXXX-XXXX-CCC0-PO287CS24CA4", // required. Hardware device ID used in /registerDevice API "tags": { // required "Level": { // Tag name "operation": "increment", // overwrites the integer tag in decrement of the following value "value": -1 // decrement for the tag value } } } } ``` ### Append List tag values To **extend the List Tag with new values**, use the `operation` parameter with the “append” value as follows: Example ```json { "request": { "hwid": "3d124a79XXXXf189XXXX7dfd9XXXXafd", // required. Hardware device ID used in /registerDevice API "application": "6XXXX-XXXX3", // required. Pushwoosh application code "tags": { // required. "ListTag": { // Tag name "operation": "append", // appends following values to the Tag's list of values "value": [ // values to append "tag2", "tag3" ] } } } } ``` ### Remove List tag values To remove some values from the List Tag, use the “remove” operation as follows: ```json { "request":{ "application": "12345-67890", // required. Pushwoosh application code "hwid": "21AB7628-XXXX-XXXX-CCC0-PO287CS24CA4", // required. Hardware device ID used in /registerDevice API "tags": { // required. "In-app Product": { // Tag name "operation": "remove", // removes the following values from the list tag "value": "outwear_02" // value or values to remove } } } } ``` ### Set tags by UserID To set tags for all devices associated to a particular [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id), use the “userId” parameter instead of “hwid”. Caution Make sure the tag you’re setting values for is [user-specific](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/#tag-scope-general-vs-user-specific). Example ```json { "request":{ "application": "AAAAA-BBBBB", // Pushwoosh app code "userId": "some_user", // user ID you'd like to set tags for "tags": { // tags and values to set "Language": "es" } } } ``` Note For emails call [`/setEmailTags`](https://docs.pushwoosh.com/developer/api-reference/email-api/#setemailtags). **Status codes:** | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Tags have been successfully set | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | ## getTags `POST` `https://api.pushwoosh.com/json/1.3/getTags` Retrieves a list of tags with corresponding values for the specific device. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | userId | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to be used instead of “hwid”. If used together with a “hwid”, the “hwid” prevails. | | hwid | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in `/registerDevice` request. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "result": { "Language": "fr" } } } ``` Example ```json { "request":{ "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "HWID", // optional. Hardware device ID used in /registerDevice API "userId": "USER_ID" // optional. Can be used instead of "hwid" to retrieve tags for a specific user } } ``` ## setBadge `POST` `https://api.pushwoosh.com/json/1.3/setBadge` Caution This endpoint is disabled. The server no longer stores or acts on the badge value it receives. Badge state is managed entirely on the device side. This call is a no-op, kept only for backward compatibility with older SDK versions that still send it. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in /registerDevice request. | | badge* | integer | Current badge on the application. | * 200 ```json { "status_code": 200, "status_message": "OK" } ``` Example ```json { "request":{ "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16dXXXXe7a6XXXX9614XXXX0fb2", // required. Hardware device ID used in /registerDevice API "badge": 4 // required. Current badge on the application } } ``` This method **IS NOT** used to update the badge value on the device. Instead please use [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) request with the `"ios_badges"` parameter. ## applicationOpen `POST` `https://api.pushwoosh.com/json/1.3/applicationOpen` Registers an app open event. Called internally from the SDK. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | -------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in `/registerDevice` request. | * 200 ```json { "status_code": 200, "status_message": "OK" } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16dXXXXe7a6XXXX9614eXXXXfb2" // required. Hardware device ID used in /registerDevice API } } ``` ## pushStat `POST` `https://api.pushwoosh.com/json/1.3/pushStat` Registers a push open event. Called internally from the SDK. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in /registerDevice request. | | userId | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to associate with the push open event. | | hash | string | Hash tag received in push notification (“p” parameter of the push payload). | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16dfXXXX7a6beXXXX14e1530fb2", // required. Hardware device ID used in /registerDevice API "userId": "USER012345", // optional. The user id to associate with the push open event "hash": "HASH_TAG" // optional. Hash tag received in push notification // ("p" parameter in the push payload) } } ``` ## messageDeliveryEvent `POST` `https://api.pushwoosh.com/json/1.3/messageDeliveryEvent` Registers push delivery event for the device. Called internally from the SDK. ##### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | ##### Request body | Name | Type | Description | | ------------- | ------ | -------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid* | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in `/registerDevice` request. | | hash | string | Hash tag received in push notification (“p” parameter of the push payload). | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16dfXXXX7a6bece9XXXX1530fb2", // required. Hardware device ID used in /registerDevice API "hash": "HASH_TAG" // optional. Hash tag received in push notification // ("p" parameter in the push payload) } } ``` --- # Email API **URL:** https://docs.pushwoosh.com/developer/api-reference/email-api/ Create email messages, register email addresses, and manage email-related operations using Email API methods. --- The Email API manages email addresses as contacts in a Pushwoosh application. Use it to register and remove email addresses, set tags on them, and link an address to a [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id). /createEmailMessage is deprecated New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — pass `platforms: ["EMAIL"]` and an [`email_payload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) block to `Notify`. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/#from-createemailmessage). ## createEmailMessage Deprecated Creates an email message. `POST` `https://api.pushwoosh.com/json/1.3/createEmailMessage` ### Request body parameters | Name | Type | Required | Description | | ------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------ | | auth | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | notifications | `array` | Yes | JSON array containing email message details. See the **Notifications Parameters** table below. | Tip Pass an `X-Request-ID` header to correlate the created message with your request for end-to-end tracing — see [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id). #### Notifications parameters | Name | Type | Required | Description | | ------------------------------ | -------------------- | :------------------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | send_date | `string` | Yes | Defines when to send the email. Format: `YYYY-MM-DD HH:mm` or `"now"`. | | preset | `string` | Yes | [Email preset code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#email-content-code). Copy from the URL bar of the **Email Content Editor** in the Pushwoosh Control Panel. | | subject | `string` or `object` | No | Subject line of the email. The email will always be in the language of the content. If `subject` does not contain a matching language for `content`, the subject will be empty. | | content | `string` or `object` | No | The email body content. Can be a string for plain HTML content or an object for localized versions. | | attachments | `array` | No | The email attachments. Only two attachments are available. Each attachment must not exceed 1MB (base64 encoded). | | list_unsubscribe | `string` | No | Allow to set custom URL for “Link-Unsubscribe” header. | | campaign | `string` | No | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to associate the email with a specific campaign. | | campaign_name | `string` | No | Internal name for this message, shown as the row title in Message History and in its export. Unrelated to `campaign` above. It doesn’t group messages or affect delivery. Max 255 characters. Empty falls back to the email’s own subject line. | | ignore_user_timezone | `boolean` | No | If `true`, sends the email immediately, ignoring user timezones. | | timezone | `string` | No | Sends the email according to the user’s timezone. Example: `"America/New_York"`. | | filter | `string` | No | Sends the email to users matching a [specific filter condition](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-name). | | devices | `array` | No | List of email addresses (max 1000) to send targeted emails. If used, the message is sent only to these addresses. Ignored if the Application Group is used. | | use_auto_registration | `boolean` | No | If `true`, automatically registers emails from the `devices` parameter. | | users | `array` | No | If set, the email message will only be delivered to the specified [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id)s (registered via /registerEmail call). Not more than 1000 user IDs in an array. If the “devices” parameter is specified,the “users” parameter will be ignored. | | dynamic_content_placeholders | `object` | No | Placeholders for dynamic content instead of device tag values. | | conditions | `array` | No | Segmentation conditions using tags. Example: `[["Country", "EQ", "BR"]]`. | | from | `object` | No | Specify a custom sender name and email, overriding the default in application properties. | | reply-to | `object` | No | Specify a custom reply-to email, overriding the default in application properties. | | bcc | `array` | No | BCC (Blind Carbon Copy): array of email addresses that receive a copy of the email without other recipients seeing them. | | email_type | `string` | No | Specify the email type: `"marketing"` or `"transactional"`. If omitted, the message is treated as marketing and is not delivered to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) members. Set `"transactional"` to deliver to everyone, including the control group. | | email_category | `string` | Required when `email_type` is `"marketing"`. | Specify one of the category names configured in the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) (e.g. Newsletter, Promotional, Product Updates). | | transactionId | `string` | No | Unique message identifier to prevent re-sending in case of network problems. Stored on the side of Pushwoosh for 5 minutes. | | capping_days | `integer` | No | The number of days (max 30) to apply frequency capping per device. **Note:** Ensure [Global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) is configured in the Control Panel. | | capping_count | `integer` | No | The max number of emails that can be sent from a specific app to a particular device within a `capping_days` period. In case the message created exceeds the `capping_count` limit for a device, it won’t be sent to that device. | | capping_exclude | `boolean` | No | If set to `true`, this email will not be counted towards the capping for future emails. | | capping_avoid | `boolean` | No | If set to `true`, capping will not be applied to this specific email. | | send_rate | `integer` | No | Limit how many messages can be sent per second across all users. Helps prevent backend overload during high-volume sends. | | send_rate_avoid | `boolean` | No | If set to true, throttling limit will not be applied to this specific email. | ### Request example ```json { "request": { "auth": "API_ACCESS_TOKEN", // required. API access token from Pushwoosh Control Panel "application": "APPLICATION_CODE", // required. Pushwoosh application code. "notifications": [{ "send_date": "now", // required. YYYY-MM-DD HH:mm OR 'now' "preset": "ERXXX-32XXX", // required. Copy Email preset code from the URL bar of // the Email Content editor page in Pushwoosh Control Panel. "subject": { // optional. Email message subject line. "de": "subject de", "en": "subject en" }, "content": { // optional. Email body content. "de": "de Hello, moto", "default": "default Hello, moto" }, "attachments": [{ // optional. Email attachments "name": "image.png", // "name" - file name "content": "iVBANA...AFTkuQmwC" // "content" - base64 encoded content of the file }, { "name": "file.pdf", "content": "JVBERi...AFTarEGC" }], "list_unsubscribe": "URL", // optional. Allow to set custom URL for "Link-Unsubscribe" header "campaign": "CAMPAIGN_CODE", // optional. To assign this email message to a particular campaign, // add a campaign code here. "campaign_name": "Black Friday", // optional. Internal name for this message, shown as // the row title in Message History and in its // export. Max 255 characters. Empty falls back // to the email's own subject line. "ignore_user_timezone": true, // optional. "timezone": "America/New_York", // optional. Specify to send the message according to // timezone set on user's device. "filter": "FILTER_NAME", // optional. Send the message to specific users meeting filter conditions. "devices": [ // optional. Specify email addresses to send targeted email messages. "email_address1", // Not more than 1000 addresses in an array. "email_address2" // If set, the message will only be sent to the addresses on ], // the list. Ignored if the Application Group is used. "use_auto_registration": true, // optional. Automatically register emails specified in "devices" parameter "users": [ // optional. If set, the email message will only be delivered to the "userId1", // specified user IDs (registered via /registerEmail call). "userId2" // Not more than 1000 user IDs in an array. ], // If the "devices" parameter is specified, // the "users" parameter will be ignored. "dynamic_content_placeholders": { // optional. Placeholders for dynamic content instead of device tag values. "firstname": "John", "firstname_en": "John" }, "conditions": [ // optional. Segmentation conditions, see remark below. ["Country", "EQ", "BR"], ["Language", "EQ", "pt"] ], "from": { // optional. Specify a sender name and sender email address "name": "alias from", // to replace the default "From name" and "From email" "email": "from-email@email.com" // set up in application properties. }, "reply-to": { // optional. Specify an email address to replace the "name": "alias reply to ", // default "Reply to" set up in application properties. "email": "reply-to@email.com" }, "bcc": [ // optional. BCC: array of email addresses that receive a copy without other recipients seeing them. "bcc1@example.com", "bcc2@example.com" ], "email_type": "marketing", // optional. "marketing" or "transactional". // If omitted, treated as marketing: not delivered to control group members. // Set "transactional" to deliver to everyone. "email_category": "category name",// required when email_type is "marketing". Category name. "transactionId": "unique UUID", // optional. Unique message identifier to prevent re-sending // in case of network problems. Stored on the side // of Pushwoosh for 5 minutes. // Frequency capping params. Ensure that Global frequency capping is configured in the Control Panel. // Frequency capping does not apply to transactional messages. // In all other cases, including omitted "email_type", frequency capping applies. "capping_days": 30, // optional. Amount of days for frequency capping (max 30 days) "capping_count": 10, // optional. The max number of emails that can be sent from a // specific app to a particular device within a 'capping_days' // period. In case the message created exceeds the // 'capping_count' limit for a device, it won't // be sent to that device. "capping_exclude": true, // optional. If set to true, this email will not // be counted towards the capping for future emails. "capping_avoid": true, // optional. If set to true, capping will not be applied to // this specific email. "send_rate": 100, // optional. Throttling limit. // Limit how many messages can be sent per second across all users. // Helps prevent backend overload during high-volume sends. "send_rate_avoid": true, // optional. If set to true, throttling limit will not be applied to // this specific email. }] } } ``` ### Response examples * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` * 210 ```json { "status_code": 210, "status_message": "Preset content is not approved", "response": null } ``` * 403 ```json { "status_code": 403, "status_message": "Token restrictions forbid this operation", "response": null } ``` Note A `210` with `"Preset content is not approved"` means [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account and the `preset` isn’t **Approved** yet. Submit and approve it from **Content** → **Email content** before sending. ### Tag conditions Each tag condition is an array like `[tagName, operator, operand]` where * tagName: name of a tag * operator: “EQ” | “IN” | “NOTEQ” | “NOTIN” | “LTE” | “GTE” | “BETWEEN” * operand: string | integer | array | date #### Operand description * EQ: tag value is equal to operand; * IN: tag value intersects with operand (operand must always be an array); * NOTEQ: tag value is not equal to an operand; * NOTIN: tag value does not intersect with operand (operand must always be an array); * GTE: tag value is greater than or equal to operand; * LTE: tag value is less than or equal to operand; * BETWEEN: tag value is greater than or equal to min operand value but less than or equal to max operand value (operand must always be an array). #### String tags Valid operators: EQ, IN, NOTEQ, NOTIN\ Valid operands: * EQ, NOTEQ: operand must be a string; * IN, NOTIN: operand must be an array of strings like `["value 1", "value 2", "value N"]`; #### Integer tags Valid operators: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE\ Valid operands: * EQ, NOTEQ, GTE, LTE: operand must be an integer; * IN, NOTIN: operand must be an array of integers like `[value 1, value 2, value N]`; * BETWEEN: operand must be an array of integers like `[min_value, max_value]`. #### Date tags Valid operators: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE\ Valid operands: * `"YYYY-MM-DD 00:00"` (string) * unix timestamp `1234567890` (integer) * `"N days ago"` (string) for operators EQ, BETWEEN, GTE, LTE #### Boolean tags Valid operators: EQ\ Valid operands: `0, 1, true, false` #### List tags Valid operators: IN\ Valid operands: operand must be an array of strings like `["value 1", "value 2", "value N"]`. Danger Remember that “filter” and “conditions” parameters should not be used together.\ Also, both of them **will be ignored**, if the “devices” parameter is used in the same request. Note **Country and Language tags** Language tag value is a lowercase two-letter code according to [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)\ Country tag value is an UPPERCASE two-letter code according to [ISO_3166-2](https://en.wikipedia.org/wiki/ISO_3166-2)\ For example, to send push a notification to Portuguese-speaking subscribers in Brazil, you will need to specify the following condition: `"conditions": [["Country", "EQ", "BR"],["Language", "EQ", "pt"]]` ## registerEmail Registers email address for the app. `POST` `https://api.pushwoosh.com/json/1.3/registerEmail` #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Type | Description | | ------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | email* | string | Email address. | | language | string | Language locale to associate with the contact. There’s no device to read it from, so pass it explicitly here, or set it later via [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). A contact left without it isn’t counted for any language and gets the default-language email. Must be a lowercase two-letter code according to ISO-639-1 standard. | | userId | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to associate with the email address. | | tz_offset | integer | Timezone offset in seconds. | | tags | object | Tag values to assign to the device registered. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` * 210 ```json { "status_code": 210, "status_message": "this hwid (email) is blacklisted", "response": null } ``` * 400 ```json { "status_code": 400, "status_message": "Missing required argument: email", "response": null } ``` * 403 ```json { "status_code": 403, "status_message": "Token restrictions forbid this operation", "response": null } ``` * 500 ```json { "status_code": 500, "status_message": "Internal server error", "response": null } ``` Example ```json { "request": { "application": "APPLICATION_CODE", // required. Pushwoosh application code. "email":"email@domain.com", // required. Email address to be registered. "language": "en", // optional. Language locale. "userId": "userId", // optional. User ID to associate with the email address. "tz_offset": 3600, // optional. Timezone offset in seconds. "tags": { // optional. Tag values to set for the device registered. "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1","string2"], // sets the list of values for Tags of List type "DateTag": "2024-10-02 22:11", // note the time should be in UTC "BooleanTag": true // valid values are: true, false } } } ``` #### Response codes The public API returns the outcome in `status_code`. Use the table below to decide whether a failed call should be retried. | `status_code` | Meaning | Retry? | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | `200` | Success — the email address is registered. | No — done. | | `210` | Argument/validation error — the request was understood but rejected (blacklisted address, invalid or disposable email, wrong platform for the account’s plan). See [210 error messages](https://docs.pushwoosh.com/developer/api-reference/email-api/#210-error-messages) below. | **No** — the same request returns the same `210`. Log the address and skip it. | | `400` | Malformed request — invalid JSON or a missing required field. | No — fix the request, don’t repeat it. | | `403` | Forbidden — invalid or restricted Device API token. | No — fix authorization. | | `500` | Internal server error — temporary infrastructure issue or timeout. | **Yes**, with exponential backoff — the only transient case. | Tip Retry only `500` responses, using exponential backoff — it’s the only transient case. A `210`, `400`, or `403` is final: the server understood your request and rejected it, so repeating it unchanged returns the same result. Log the address (for `210`) or fix the request/token (for `400`/`403`) instead. #### 210 error messages A `210` response carries the specific reason in `status_message`. | `status_message` | Meaning | | --------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | | `this hwid (email) is blacklisted` | The address is on the suppression list after a permanent (hard) bounce and won’t be re-registered. | | `hwid (email) is invalid` / `has invalid semantic` | The address fails validation. | | `hwid (email) is empty` | No address was provided. | | `hwid (email) has invalid count of parts` | Missing or extra `@`. | | `hwid (email) has invalid local part` | The part before `@` is invalid. | | `hwid (email) has invalid domain part` | The domain part is invalid. | | `hwid (email) has disposable domain` | The address uses a disposable/temporary email domain (e.g. 10minutemail). | | `hwid is not valid` | The `hwid` itself is malformed. | | `only email platform allowed for Email Only subscription` | The account is on an Email Only plan and can’t register non-email devices. | Note Only **permanent (hard) bounces** add an address to the blacklist. Soft bounces and spam complaints do **not** block `registerEmail` — only `this hwid (email) is blacklisted` reflects suppression. ## deleteEmail Removes email address from your user base. `POST` `https://api.pushwoosh.com/json/1.3/deleteEmail` #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Type | Description | | ----------- | ------ | ---------------------------------------------------------------------------------------------------- | | application | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | email | string | Email address used in [`/registerEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api/#registeremail) request. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "APPLICATION_CODE", // required. Pushwoosh application code "email": "email@domain.com" // required. Email to delete from app subscribers. } } ``` ## setEmailTags Sets tag values for the email address. `POST` `https://api.pushwoosh.com/json/1.3/setEmailTags` #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Type | Description | | ----------- | ------ | ----------------------------------------------------------------------------------------------- | | application | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | email | string | Email address. | | tags | object | JSON object of tags to set, send ‘null’ to remove the value. | | userId | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) associated with the email address. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "skipped": [] } } ``` Example ```json { "request": { "email": "email@domain.com", // required. Email address to set tags for. "application": "APPLICATION_CODE", // required. Pushwoosh application code. "tags": { "StringTag": "string value", "IntegerTag": 42, "ListTag": ["string1", "string2"], "DateTag": "2024-10-02 22:11", // time in UTC "BooleanTag": true // valid values are: true, false }, "userId": "userId" // optional. User ID associated with the email address. } } ``` Note For other device types will be returned 200 OK, though tags won’t be saved. Caution Please avoid setting more than 50 tag values in a single `/setEmailTags` request. ## registerEmailUser Associates an external [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) with a specified email address. `POST` `https://api.pushwoosh.com/json/1.3/registerEmailUser` Note Please note that this method **does not register an email address** in your user base; it should be used only for assigning user IDs to email addresses that have been registered already by `/registerEmail` request. Can be used in `/createEmailMessage` API call (the ‘users’ parameter). #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Type | Description | | ------------- | ------- | ------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | email* | string | Email address. | | userId* | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to associate with the email address. | | tz_offset | integer | Timezone offset in seconds. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` * 400 ```json { "status_code": 400, "status_message": "Request format is not valid." } ``` * 403 ```json { "status_code": 403, "status_message": "Forbidden." } ``` Example ```json { "request": { "application": "APPLICATION_CODE", // required. Pushwoosh application code. "email": "email@domain.com", // required. User email address. "userId": "userId", // required. User ID to associate with the email address. "tz_offset": 3600 // optional. Timezone offset in seconds. } } ``` Note To retrieve data on soft bounces, hard bounces, and email complaints, including the date, email address, and reason for each bounce, use the [BouncedEmails](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#bouncedemails) method. --- # Email Templates API **URL:** https://docs.pushwoosh.com/developer/api-reference/email-templates-api/ Create, retrieve, list, update, delete, and clone Pushwoosh email templates through a REST API. --- The Email Templates API manages the reusable email templates behind an application’s email presets — the same templates you build in the Control Panel’s email editor. Each template stores per-locale subjects, sender info, and editor content, and is identified by the code of the email preset it’s connected to. Use that code to send the template through [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) (email payload [`email_template`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/#emailpayload)) or a Customer Journey [Send email point](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#messaging-points). ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise. ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` ## Conventions * **Field naming:** request bodies and query/path parameters accept `lowerCamelCase` (for example, `previewSettings`, `searchByLabel`, `includeHtml`) — the server unmarshals either casing. Responses are always marshaled using the proto field names, in `snake_case` (`per_page`, `email_template`, `sender_info`, `preview_settings`, and so on). The response examples and the [object reference](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#object-reference) below use that casing. * **`code`:** every template response carries the code of its connected email preset, not an internal template ID. Pass this same code to `Get`, `Update`, `Delete`, and to the messaging/journey APIs above. * **Unpopulated fields:** responses include all fields, even when empty or zero-valued. ### Error responses | HTTP status | Meaning | | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | `400 Bad Request` | Invalid argument — a required field is missing or malformed, or a precondition failed (for example, deleting a template still used by a journey). | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application or preset does not belong to the caller’s account. | | `404 Not Found` | The template, preset, or application was not found. | | `500 Internal Server Error` | Unexpected server-side failure. | ## Endpoints | Method | Path | Description | | :------- | :---------------------------- | :------------------------------------------ | | `POST` | `/api/email_templates` | Create a new email template | | `GET` | `/api/email_templates` | List an application’s email templates | | `GET` | `/api/email_templates/{code}` | Get a single email template | | `PUT` | `/api/email_templates/{code}` | Update an email template | | `DELETE` | `/api/email_templates/{code}` | Delete an email template | | `POST` | `/api/email_templates:clone` | Clone an email template into an application | ## Create Creates a new email template — its editor content plus a connected email preset — in an application, and returns the generated template code. `POST` `/api/email_templates` ### Request body | Parameter | Type | Required | Description | | :---------------- | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `application` | string | Yes | The [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) to create the template in. | | `name` | string | Yes | Template name, 1–255 characters. | | `content` | object | Yes | The [email content object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-content-object). | | `label` | string | No | Free-text label, up to 255 characters. | | `categories` | array of strings | No | Category names to tag the template with. | | `previewSettings` | object | No | Arbitrary editor preview settings, stored and returned as-is. | | `system` | boolean | No | Marks the template as a system template — an internal feature, e.g. a synced-block fragment. System templates are hidden from `List` (see note below), but stay reachable by code. Defaults to `false`. | ##### Request example ```json { "application": "XXXXX-XXXXX", "name": "Welcome email", "label": "onboarding", "categories": ["lifecycle"], "content": { "senderInfo": { "from": { "email": "hello@acme.com", "name": "Acme" }, "replyTo": { "email": "support@acme.com", "name": "Acme Support" } }, "subject": { "en": "Welcome to Acme!", "default": "Welcome to Acme!" }, "pushwoosh": { "html": "Welcome, {name|string|there}!", "localizationData": { "default": { "name": "there" } } } } } ``` ### Response Returns `{ "email_template": { ... } }` — the created [email template object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-template-object), but **without** `content` (this endpoint doesn’t echo it back). Call [Get](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#get) with the returned `code` if you need to read the content back. ## List Lists an application’s email templates — metadata only, no content — with paging, ordering, and filtering by name, label, or category. `GET` `/api/email_templates` ### Query parameters | Parameter | Type | Required | Description | | :-------------------- | :--------------- | :------- | :---------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | The application code to list templates for. | | `orderBy` | string | No | `NAME` (default), `CREATED`, or `UPDATED`. | | `orderDirection` | string | No | `ASC` (default) or `DESC`. | | `page` | integer | No | Zero-based page index. | | `perPage` | integer | No | Page size. Defaults to `100` when omitted or `0`. This endpoint doesn’t enforce an explicit maximum. | | `searchByName` | string | No | Substring match (`like %value%`) against the template’s name **or** its code — either matching is enough. | | `searchByLabel` | string | No | Substring match on label (`like %label%`), or exact match when `strictSearchByLabel` is `true`. | | `strictSearchByLabel` | boolean | No | Use exact-match instead of substring for `searchByLabel`. | | `searchByCategory` | array of strings | No | Repeat the parameter to filter by any of several categories, e.g. `?searchByCategory=lifecycle&searchByCategory=promo`. | Note System templates (created with `system: true`) are never returned by `List`, regardless of filters. ### Response | Field | Type | Description | | :---------------- | :--------------- | :------------------------------------------------------------------------------------------------------- | | `email_templates` | array of objects | The current page of [email template objects](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-template-object). `content` is `null` on every item. | | `page` | integer | The returned page index. | | `per_page` | integer | The page size used for this response. | | `total` | integer | Total number of templates matching the filters, across all pages. | ##### Response example ```json { "email_templates": [ { "code": "AAAAA-BBBBB", "name": "Welcome email", "label": "onboarding", "categories": ["lifecycle"] } ], "page": 0, "per_page": 100, "total": 1 } ``` ## Get Returns a single email template by its code, including sender info, per-locale subjects, and the full editor content. `GET` `/api/email_templates/{code}` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :----------------------------------------------------- | | `code` | string | The template’s code (its connected email preset code). | ### Query parameters | Parameter | Type | Required | Description | | :------------ | :------ | :------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `includeHtml` | boolean | No | Whether to return the rendered `html` alongside the editor content. Defaults to `true`. Set to `false` to skip it — it’s typically over half the payload, and the editor content already describes the template. | ### Response Returns `{ "email_template": { ... } }`, the full [email template object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-template-object). ## Update Updates an existing email template by code, overwriting the supplied fields. `PUT` `/api/email_templates/{code}` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :----------------------------- | | `code` | string | The template’s code to update. | ### Request body | Parameter | Type | Required | Description | | :---------------- | :--------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------- | | `name` | string | No | New name, 1–255 characters. Omit to keep the current name. | | `content` | object | No | New [email content object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-content-object), replacing the stored content in full. Omit to leave the content unchanged. | | `label` | string | No | New label. Always overwritten — omit or send `""` to clear it. | | `categories` | array of strings | No | New full set of category names. Omit to leave categories unchanged; send `[]` to clear them. | | `previewSettings` | object | No | New preview settings. Omit to leave unchanged. | ##### Request example ```json { "name": "Welcome email v2", "label": "onboarding", "content": { "senderInfo": { "from": { "email": "hello@acme.com", "name": "Acme" } }, "subject": { "default": "Welcome to Acme — updated!" }, "pushwoosh": { "html": "...", "localizationData": {} } } } ``` ### Response Returns `{ "email_template": { ... } }` — the updated [email template object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-template-object), also **without** `content`. Call [Get](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#get) if you need to read the content back. ## Delete Deletes an email template and its connected preset by code, removing the stored content. `DELETE` `/api/email_templates/{code}` Caution Fails with `400 Bad Request` if the template is used by a `send_email` point in a running or paused journey — remove or repoint that point first via [Update](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/create-update/#update). This cannot be undone. ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :----------------------------- | | `code` | string | The template’s code to delete. | ### Response An empty object on success: `{}`. ## Clone Clones an email template — its content and preset — into a destination application, optionally under a new name. `POST` `/api/email_templates:clone` Note Only the `default` (or `en`, used as a fallback) locale subject is preserved on the clone; other localized subjects are dropped. ### Request body | Parameter | Type | Required | Description | | :---------------- | :----- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `emailPresetCode` | string | Yes | The template’s `code` (as returned by `Create`, `Get`, `List`, or `Update`) to clone. Named `emailPresetCode` here because it’s the code of the connected email preset — see [Conventions](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#conventions). | | `application` | string | Yes | Destination application code. Can be the same application, or a different one owned by the same account. | | `name` | string | No | Name for the clone, 1–255 characters. Defaults to the source template’s name. | ##### Request example ```json { "emailPresetCode": "AAAAA-BBBBB", "application": "YYYYY-YYYYY", "name": "Welcome email (copy)" } ``` ### Response | Field | Type | Description | | :------------------ | :----- | :----------------------------------------------------------------------------------- | | `email_preset_code` | string | The new template’s `code` — the same identifier `Get`/`Update`/`Delete` call `code`. | ## Object reference Field names below match what `Get`, `List`, `Update`, and `Create` actually return — `snake_case` proto field names (see [Conventions](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#conventions)). When you send these same structures back in a request body (`Create`, `Update`), the `lowerCamelCase` form used in the request examples above works too; the server accepts either casing on input. ### Email template object | Field | Type | Description | | :----------------- | :---------------- | :------------------------------------------------------------------------------------------------------------------------------ | | `code` | string | Code of the connected email preset. Identifies this template everywhere else in the API. | | `name` | string | Template name. | | `label` | string | Free-text label. | | `categories` | array of strings | Category names. | | `content` | object | The [email content object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#email-content-object). Populated only by `Get`; `null` in `Create`, `List`, and `Update` responses. | | `preview_settings` | object | Arbitrary editor preview settings. | | `created` | string (RFC 3339) | Creation timestamp. | | `updated` | string (RFC 3339) | Last update timestamp. | ### Email content object | Field | Type | Description | | :------------------------------------- | :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `sender_info` | object | [Sender info object](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#sender-info-object) — `from` and `reply_to` addresses. | | `subject` | object (map) | Per-locale subject, e.g. `{ "en": "Subject", "default": "Subject" }`. | | `unlayer` / `pushwoosh` / `smartcards` | object | The editor content. **Exactly one** of these must be set — it selects which editor produced (and will render) the template. See [editor kinds](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#editor-kinds) below. | Caution The proto also defines a `raw` editor kind (per-locale HTML with no editor state). The API currently rejects it on `Create`/`Update` with `400 Bad Request: raw content not supported` — don’t use it. #### Editor kinds | Kind | Field | Required sub-fields | Description | | :----------- | :------------------------------------------- | :----------------------------------- | :-------------------------------------------------------------------------------------- | | `unlayer` | `html`, `localization_data`, `editor_config` | `editor_config`, `localization_data` | Drag-and-drop block editor (Unlayer). `editor_config` is the Unlayer design JSON. | | `pushwoosh` | `html`, `localization_data` | `localization_data` | Pushwoosh’s own HTML-based editor. Recommended for programmatic/API-authored templates. | | `smartcards` | `html`, `localization_data`, `content` | `content`, `localization_data` | Smart Cards block editor; `content` is its editor-specific JSON. | In every kind, `html` is the rendered output. `localization_data` is that editor’s own per-locale content: an object keyed by locale code (`en`, `es`, `default`, …), where each value is that locale’s copy of the editor’s fields. Its inner shape is editor-specific and opaque to this API — the API stores and returns it as-is. It’s required on `Create`/`Update` for every kind (send `{}` if there’s nothing to localize). Note Whenever the API returns `content` (currently only [Get](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#get), since [Create](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#create) and [Update](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#update) don’t echo it back), it guarantees one locale is exposed as `default`: if you didn’t send a `default` key yourself, it promotes `en` (or, failing that, whichever locale key comes first) to `default`, renaming it — every other locale you sent is kept as-is. `subject` gets the same treatment. This is different from [Clone](https://docs.pushwoosh.com/developer/api-reference/email-templates-api/#clone), which keeps only that one default-locale value and drops the rest. This rename is only applied when building the response — but it’s not undone on write. If you take a `Get` response as-is and send it straight to `Update`, the promoted key (e.g. `en`) is now literally `default`, and `Update` stores exactly what you send: the original `en` key is gone from then on, permanently. If you need to keep the original locale key, rename `default` back to it yourself before calling `Update`. Text inside `html` or a `localization_data` value can include [Dynamic Content](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/) tags, e.g. `{name|string|there}` — those resolve against the recipient’s device Tags when the email is actually sent. This API doesn’t resolve them; it just stores and returns whatever text you put there. ### Sender info object | Field | Type | Description | | :--------- | :----- | :-------------------------------------------------------- | | `from` | object | `{ "email": string, "name": string }` — sender address. | | `reply_to` | object | `{ "email": string, "name": string }` — reply-to address. | Both `email` sub-fields, when non-empty, must be valid email addresses. ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Email payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) [Customer Journey point reference ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#messaging-points/) --- # Events API **URL:** https://docs.pushwoosh.com/developer/api-reference/events/ Create and manage events using API methods to track user interactions and trigger automated workflows. --- The Events API creates an event in a Pushwoosh application together with its description and attributes. Creating an event in advance is optional. Use it when you want to set the attribute types yourself, because `/postEvent` creates a missing event automatically and detects attribute types from the values it receives. Note To track events triggered by users, call the [/postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method from User Centric API. ## createEvent `POST` `https://api.pushwoosh.com/json/1.3/createEvent` Creates an Event for the app. #### Request Body | Name | Type | Description | | --------------- | ------- | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | name | string | Event name, 128 characters max. | | description | string | Event description, 512 characters max. | | eventAttributes | array | Event attributes array. | | trackRevenue | boolean | See Track Event Revenue section. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "event": 351 // Created Event Id } } ``` ```json { "request": { "auth":"yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "name": "BasketOpen", // required. 128 characters max. Event name. "description": "Open basket page", // optional. 512 characters max. Event description. "eventAttributes": [{ // optional. Event attributes array "type": 3, // Attribute type. Values: // 1 - integer; // 2 - string; // 3 - list; // 4 - date; // 5 - boolean; // 6 - price; "name": "products" // 255 characters max. Attribute name }, { "type": 5, "name": "delivery" }], "trackRevenue": false } } ``` **Response:** | | | | | --- | --- | -------------------------------------------------------------- | | 200 | 200 | Event successfully created | | 200 | 210 | Argument error. See status_message for more info | | 200 | 403 | Permissions error (for example, when Events limit is exceeded) | | 200 | 404 | Resource not found | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | --- # Feature Flags API **URL:** https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/ Create, change, kill, archive, restore, and reshuffle Pushwoosh feature flags and read their change history through the API. --- A [feature flag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) is a remote switch with properties that the SDK reads for each device. This API manages the flags of an application: the same operations as **Audience** → **Feature Flags** in the Control Panel. To read flags in a website or a mobile app, use the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/), the [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/), or the [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/). Beta feature Feature flags are in beta and available only on plans that include them. On other plans, `Create` and `Restore` fail with `FEATURE_FLAGS_NOT_ALLOWED`. ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json`. ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` Reading flags needs access to the application. Creating and changing them needs the right to manage it. ## Conventions * **Field naming:** request bodies accept `lowerCamelCase` or `snake_case`. Responses use `snake_case`, as in the [Feature flag object](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#feature-flag-object). * **Percentages** are in basis points: `percent_bp` from `0` to `10000`, where `2000` is 20%. * **Versions:** every flag carries a `version`. `Update`, `SetRulePercent`, and `Reshuffle` take the version you last read and fail with `VERSION_CONFLICT` when the flag changed since, so concurrent edits never overwrite each other. Read the flag again and retry. * **Statuses:** `active`, `killed`, or `archived`. `Kill`, `Unkill`, `Archive`, and `Restore` change only the status and take no version. ## Endpoints Each REST endpoint has an MCP tool with the same parameters: | Method | Path | Description | MCP tool | | :----- | :--------------------------------------------------------------------------------- | :----------------------------------------------- | :------------------------------ | | `GET` | `/api/applications/{application_code}/feature_flags` | List the application’s flags | `list_feature_flags` | | `GET` | `/api/applications/{application_code}/feature_flags/{key}` | Get one flag | `get_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags` | Create a flag | `create_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}` | Replace name, description, properties, and rules | `update_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/rules/{rule_id}/percent` | Change the rollout of one rule | `set_feature_flag_rule_percent` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/kill` | Turn the flag off for everyone | `kill_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/unkill` | Turn a killed flag back on | `unkill_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/archive` | Archive a flag | `archive_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/restore` | Restore an archived flag | `restore_feature_flag` | | `POST` | `/api/applications/{application_code}/feature_flags/{key}/reshuffle` | Pick a new sample of users | `reshuffle_feature_flag` | | `GET` | `/api/applications/{application_code}/feature_flags/{key}/history` | List the changes of a flag | `list_feature_flag_history` | Every endpoint except `List` and `History` returns `{ "flag": { ... } }`, the [Feature flag object](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#feature-flag-object) after the operation. ## List `GET` `/api/applications/{application_code}/feature_flags` The request takes these parameters: | Parameter | In | Type | Description | | :----------------- | :---- | :----- | :---------------------------------------------------------------------------------- | | `application_code` | path | string | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `status` | query | string | Optional. `active`, `killed`, or `archived`. Omit to list all flags. | Returns `{ "flags": [ ... ] }`, an array of [Feature flag objects](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#feature-flag-object). ## Get `GET` `/api/applications/{application_code}/feature_flags/{key}` Returns one flag with its properties, rules, and current `version`. ## Create Creates an **active** flag. The server generates the `rule_id` of every rule. `POST` `/api/applications/{application_code}/feature_flags` The request body has these fields: | Parameter | Type | Required | Description | | :------------ | :-------------------------------------------- | :--------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------- | | `key` | string | Yes | Matches `^[A-Za-z0-9_.-]{1,58}$`. Unique in the application, archived flags included, compared case-insensitively. Can’t be changed later. | | `name` | string | No | Display name, up to 255 characters. | | `description` | string | No | Description. | | `properties` | array of [Property objects](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#property-object) | No | Up to 50 properties. | | `defaults` | object | Yes, if there are properties | The value of every property for devices the flag is off for, keyed by property key. Each value must match the property type. | | `rules` | array of [Rule objects](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#rule-object) | No | Ordered rules, up to 10. The first rule whose segment matches the device decides. | ##### Request example ```json { "key": "new_checkout", "name": "New checkout", "properties": [ { "key": "button_color", "type": "string" }, { "key": "show_apple_pay", "type": "boolean" } ], "defaults": { "button_color": "blue", "show_apple_pay": false }, "rules": [ { "name": "Beta testers", "filter_code": "XXXXX-XXXXX", "percent_bp": 10000, "overrides": { "button_color": "green", "show_apple_pay": true } }, { "name": "Everyone else", "filter_code": "", "percent_bp": 2000, "overrides": { "button_color": "orange" } } ] } ``` ## Update Replaces the name, description, properties, defaults, and rules of a flag. The status never changes here. Send all fields, not only the changed ones. `POST` `/api/applications/{application_code}/feature_flags/{key}` Takes the same body as [Create](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#create) without `key`, plus `version` from your last read. Keep the `rule_id` of every existing rule. A rule without `rule_id` is created as new. A property’s type can’t change after the property is created. ## SetRulePercent Changes the rollout of one rule. Users who already have the flag keep it when the percentage grows. `POST` `/api/applications/{application_code}/feature_flags/{key}/rules/{rule_id}/percent` ```json { "percentBp": 5000, "version": 3 } ``` ## Kill and Unkill `POST` `.../feature_flags/{key}/kill` turns the flag off for every device, which then gets the default property values. Rules and properties are kept. `POST` `.../feature_flags/{key}/unkill` turns a killed flag back on with its rules as they were. Both take an empty body `{}`. ## Archive and Restore `POST` `.../feature_flags/{key}/archive` stops sending the flag to devices. An archived flag doesn’t count toward the plan’s limit of flags per application, and its key stays reserved. `POST` `.../feature_flags/{key}/restore` brings an archived flag back as `killed`, so it never turns on by itself. Call `unkill` to roll it out again. Both take an empty body `{}`. ## Reshuffle Places every user in a new bucket, so the same percentages select a different sample of users. Some users who have the flag on lose it, and others get it. `POST` `/api/applications/{application_code}/feature_flags/{key}/reshuffle` ```json { "confirm": true, "version": 3 } ``` `confirm` must be `true`. Otherwise the request fails with `CONFIRM_REQUIRED`. ## History Returns the changes of a flag, newest first: who made each change, when, and which fields changed. Like `Get`, it needs access to the application, and it returns `404` when the application or the flag doesn’t exist. `GET` `/api/applications/{application_code}/feature_flags/{key}/history` The request takes these parameters: | Parameter | In | Type | Description | | :----------------- | :---- | :------ | :---------------------------------------------------------------------------------- | | `application_code` | path | string | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `key` | path | string | The flag key. | | `page` | query | integer | Optional. Page number, starting from `0`. | | `per_page` | query | integer | Optional. Entries per page, `20` by default, up to `100`. | Returns `{ "entries": [ ... ], "total": N }`, where `total` is the number of entries across all pages. Each entry has these fields: | Field | Type | Description | | :--------------- | :------------------------------------------ | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `version` | integer | The flag version this change produced. | | `action` | string | `create`, `update`, `set_percent`, `kill`, `unkill`, `archive`, `restore`, or `reshuffle`. | | `user_id` | string | The ID of the Control Panel user who made the change. `0` for a change made with an API token or by a Pushwoosh service. | | `user_email` | string | That user’s email. Empty when the user was deleted or `user_id` is `0`. | | `by_api_token` | boolean | `true` when the change was made with an API token. `false` for a change by a user or by a Pushwoosh service. | | `api_token_name` | string | The name the API token had at the time of the change. It stays after the token is deleted. Empty when the token has no name or `by_api_token` is `false`. | | `created` | string | Time of the change, RFC 3339. | | `flag` | [Feature flag object](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#feature-flag-object) | The flag after the change. | | `changes` | array | The fields that differ from the previous version. Empty for `create` and `reshuffle`, and for the oldest entry of a flag created before history was recorded, because there is no earlier version to compare it with. | Each item of `changes` has `field`, `before`, and `after`. `before` and `after` hold the values encoded as JSON strings, so a string value comes in quotes. `field` takes these values: | `field` | What changed | | :------------------------------ | :------------------------------------------------------------------------------------ | | `name`, `description`, `status` | The flag field with this name. | | `properties` | The properties and their default values. | | `rules[]` | One rule. For an added rule, `before` is empty. For a removed rule, `after` is empty. | | `rules.order` | The order of the rules. | ##### Response example ```json { "entries": [ { "version": 4, "action": "set_percent", "user_id": "12345", "user_email": "owner@example.com", "by_api_token": false, "api_token_name": "", "created": "2026-10-14T09:30:00Z", "flag": { "key": "new_checkout", "status": "active", "version": 4 }, "changes": [ { "field": "rules[r_1a2b3c4d]", "before": "{\"rule_id\":\"r_1a2b3c4d\",\"name\":\"Beta testers\",\"filter_code\":\"XXXXX-XXXXX\",\"percent_bp\":2000,\"overrides\":{\"button_color\":\"green\"}}", "after": "{\"rule_id\":\"r_1a2b3c4d\",\"name\":\"Beta testers\",\"filter_code\":\"XXXXX-XXXXX\",\"percent_bp\":5000,\"overrides\":{\"button_color\":\"green\"}}" } ] } ], "total": 4 } ``` The `flag` object in the example is shortened. History is recorded for changes from October 13, 2026. A flag with no recorded changes returns no entries. ## Read flags for a device The SDKs read flags from a separate endpoint. You don’t call it when you use the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/), the [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/), or the [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/), because they pick the host and cache the answer themselves. `POST` `https://api.pushwoosh.com/api/v2/device-api/getFeatureFlags` The request body has these fields: | Field | Type | Description | | :------------ | :----- | :---------------------------------------------------------------------------------- | | `application` | string | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `hwid` | string | The device’s [HWID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id). | | `etag` | string | Optional. The `etag` of the last answer the device holds. | The response has these fields: | Field | Type | Description | | :------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `flags` | object | Flags keyed by flag key. Each has `enabled`, `rule_id`, and `properties`. `rule_id` is `default` when no rule matched and empty for a killed flag. Archived flags aren’t included. | | `etag` | string | Send it with the next request. | | `not_modified` | boolean | `true` when the sent `etag` is current. `flags` is then omitted and the device keeps its copy. | | `partial` | boolean | `true` when the device’s segments weren’t matched in time. Don’t cache this answer. | On a plan without feature flags, the response is empty. ## Objects ### Feature flag object Every endpoint returns the flag in this shape: | Field | Type | Description | | :------------------------- | :-------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | | `application_code` | string | The application the flag belongs to. | | `key` | string | The flag key. | | `name` | string | Display name. | | `description` | string | Description. | | `status` | string | `active`, `killed`, or `archived`. | | `properties` | array of [Property objects](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#property-object) | The flag’s properties. | | `defaults` | object | The value of every property for devices the flag is off for. | | `rules` | array of [Rule objects](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#rule-object) | Ordered rules. | | `version` | integer | Send it back with `Update`, `SetRulePercent`, and `Reshuffle`. | | `created` | string | Creation time, RFC 3339. | | `updated` | string | Last change time, RFC 3339. | | `last_modified_by_user_id` | string | The Control Panel user who changed the flag last. `0` when the last change was made with an API token or by a Pushwoosh service. | ### Property object A property describes one value the SDK reads from the flag: | Field | Type | Description | | :----- | :----- | :------------------------------------------------------------------------------- | | `key` | string | Matches `^[A-Za-z0-9_]{1,64}$`, unique within the flag. | | `type` | string | `string`, `number`, `boolean`, or `json`. Can’t change once the property exists. | ### Rule object A rule decides who gets the flag on and with which values: | Field | Type | Description | | :------------ | :------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `rule_id` | string | Generated by the server, `r_` followed by 8 hex digits, and stable across updates. Leave it empty for a new rule. | | `name` | string | Rule name. | | `filter_code` | string | The [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) the device must match. Empty matches every device. The segment can hold only tag, location, application, and list conditions. | | `percent_bp` | integer | Share of matching users the flag is on for, `0`–`10000`. A device that matches the segment stops at this rule even when it falls outside the percentage. | | `overrides` | object | Property values that replace the defaults for devices this rule turns on, keyed by property key. | ## Errors A rejected request returns one of these HTTP status codes: | HTTP status | Meaning | | :-------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | `400 Bad Request` | The request is invalid (`field_violations`) or the flag’s state or the plan doesn’t allow it (`Type`). See the body formats and reasons below. | | `401 Unauthorized` | The `Authorization` header is missing or the token is invalid. | | `403 Forbidden` | The token can’t manage the application, or the application is a system one. | | `404 Not Found` | The application or the flag doesn’t exist. | | `409 Conflict` | Another request is changing the same flag at this moment. Retry the request. | | `500 Internal Server Error` | Unexpected server failure. The body is `{"message": "Sorry, an unexpected error occurred"}`. | An invalid request returns `400` with a list of field violations. Each violation names the field, the reason, and the arguments of the reason: ```json { "code": 3, "message": "validation failed", "details": [ { "@type": "type.googleapis.com/pushwoosh.rpc.errors.v3.BadRequest", "field_violations": [ { "field": "key", "description": "a feature flag with this key already exists in the application", "reason": "KEY_TAKEN", "args": { "key": "new_checkout" } } ] } ] } ``` A request that the flag’s state or the plan doesn’t allow returns `400` with one failed precondition. Its `Type` and `Description` fields are capitalized: ```json { "code": 9, "message": "precondition failed", "details": [ { "@type": "type.googleapis.com/pushwoosh.rpc.errors.v3.FailedPrecondition", "Type": "VERSION_CONFLICT", "Description": "feature flag was changed: version 3 was sent, the current one is 4" } ] } ``` The `reason` of a field violation and the `Type` of a failed precondition take these values: | Reason | Cause | | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `KEY_INVALID` | The key doesn’t match `^[A-Za-z0-9_.-]{1,58}$`. | | `KEY_TAKEN` | Another flag of the application, archived ones included, has this key. | | `TEXT_INVALID` | The name or description is too long or has forbidden characters. | | `PROPERTIES_LIMIT` | More than 50 properties. | | `PROPERTY_KEY_INVALID` | A property key is invalid or repeated. | | `PROPERTY_TYPE_INVALID` | A property type isn’t `string`, `number`, `boolean`, or `json`. | | `PROPERTY_TYPE_IMMUTABLE` | The request changes the type of an existing property. | | `VALUE_INVALID` | A default or override is missing, doesn’t match the property type, or is nested deeper than 5 levels. | | `PROPERTIES_TOO_LARGE` | The property values of the flag exceed 10,000 bytes. | | `RULES_LIMIT` | More than 10 rules. | | `RULE_NOT_FOUND` | The `rule_id` isn’t one of the flag’s rules. | | `PERCENT_INVALID` | `percent_bp` is outside `0`–`10000`. | | `OVERRIDE_UNKNOWN_PROPERTY` | An override names a property the flag doesn’t have. | | `SEGMENT_NOT_FOUND` | The rule segment doesn’t exist or belongs to another application. | | `INVALID_SEGMENT_TYPE` | The rule segment has a condition flags can’t evaluate: event, static, external, last update, or best time to send. | | `FLAG_DEPENDENCY` | The rule segment has a feature flag condition, and a flag can’t depend on another flag. Returned by `Create`, `Update`, and `Restore`, with the segment code in `filter_code`. | | `CONFIRM_REQUIRED` | `Reshuffle` was sent without `confirm: true`. | | `TAG_CONFLICT` | The account has a custom tag named `PW_FF_` followed by the flag key. | | `FEATURE_FLAGS_NOT_ALLOWED` | The account’s plan doesn’t include feature flags. | | `FLAGS_LIMIT` | The application has as many non-archived flags as the plan allows. | | `VERSION_CONFLICT` | The flag changed since the `version` you sent. | | `PAYLOAD_TOO_LARGE` | The flags of the application would take more than 64 KB in one device’s answer. | | `STATUS_CONFLICT` | The status doesn’t allow the operation, for example killing an archived flag. | --- # Geozones API **URL:** https://docs.pushwoosh.com/developer/api-reference/geozones/ API methods to manage Geozones, including adding, updating, deleting, and retrieving geozone data. --- The Geozones API manages geozones and geozone clusters in a Pushwoosh application. Use it to add, update, delete, and list them. The Pushwoosh SDK calls `/getNearestZone` to report the device location and get the closest geozone. ## getNearestZone Called internally from the SDK. Retrieves the parameters of the nearest geozone and the distance to it. Also records the device location for geo push notifications. ```http POST https://api.pushwoosh.com/json/1.3/getNearestZone ``` ### Request body parameters | Parameter | Type | Required | Description | | --------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------------ | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **hwid** | `string` | Yes | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in the `/registerDevice` request. | | **lat** | `string` | Yes | Latitude of the device. | | **lng** | `string` | Yes | Longitude of the device. | ### Request example ```json { "request": { "application": "APPLICATION_CODE", "hwid": "HWID", "lat": 10.12345, "lng": 28.12345 } } ``` ### PHP example ```php // See http://gomoob.github.io/php-pushwoosh/get-nearest-zone.html use Gomoob\Pushwoosh\Model\Request\GetNearestZoneRequest; // Creates the request instance $request = GetNearestZoneRequest::create() ->setHwid('HWID') ->setLat(10.12345) ->setLng(28.12345); // Call the '/getNearestZone' Web Service $response = $pushwoosh->getNearestZone($request); if ($response->isOk()) { print 'Zone name : ' . $response->getResponse()->getName(); print 'Latitude : ' . $response->getResponse()->getLat(); print 'Longitude : ' . $response->getResponse()->getLng(); print 'Range : ' . $response->getResponse()->getRange(); print 'Distance : ' . $response->getResponse()->getDistance(); } else { print 'Oops, the operation failed :-('; print 'Status code : ' . $response->getStatusCode(); print 'Status message : ' . $response->getStatusMessage(); } ``` ## addGeoZone Adds a Geozone to a specific app. ```http POST https://api.pushwoosh.com/json/1.3/addGeoZone ``` ### Request body parameters | Parameter | Type | Required | Description | | ----------------------- | ------------------ | :--------------------------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **geozones** | `array` | Yes | Geozone parameters as a JSON array. | | **geozones.name** | `string` | Yes | Geozone name. | | **geozones.lat** | `string` | Required for a circle. | Geozone latitude. Omit when `polygon` is set — a polygon geozone derives its own center. | | **geozones.lng** | `string` | Required for a circle. | Geozone longitude. Omit when `polygon` is set — a polygon geozone derives its own center. | | **geozones.cooldown** | `integer` | Yes | Silent period after sending a notification (in seconds). | | **geozones.range** | `integer` | Required for a circle. | Geozone range in meters. Minimum 50. Omit when `polygon` is set — a polygon geozone derives its own range. | | **geozones.polygon** | `object` | No | Makes the geozone a polygon instead of a circle. Cannot be combined with `lat`/`lng`/`range` — sending both is rejected. See [Polygon geozones](https://docs.pushwoosh.com/developer/api-reference/geozones/#polygon-geozones). | | **geozones.content** | `string or object` | Required if `presetCode` is empty. | Geozone message content. | | **geozones.presetCode** | `string` | Required if `content` is empty. | [Push preset](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#preset-code) to use instead of `content`. | | **geozones.cluster** | `string` | No | Specify `null` to unbind a cluster from the Geozone. | | **geozones.campaign** | `string` | No | Specify `null` to unbind a campaign from the Geozone. If omitted, the campaign value remains unchanged. Note: Has higher priority than the campaign in the preset. | | **geozones.timetable** | `object` | No | Sets timetable intervals. | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // Pushwoosh application code "geozones": [{ "name": "Statue of George", // required. Geozone name. "lat": "40.70087797", // required. Geozone latitude. "lng": "-73.931851387", // required. Geozone longitude. "cooldown": 60, // in seconds, required. Silent period after sending a notification "range": 50, // in meters, minimum 50, required. Range of the geozone. "content": "Lorem ipsum dolor sit amet, consectetur adipiscing elit.", // or object "presetCode": "AAAAA-BBBBB", // optional. Push preset could be used instead of content "cluster": "GEOZONE CLUSTER CODE", // optional. Cluster's cooldown period will be applied "campaign": "CAMPAIGN_CODE", // optional. Specify null to unbind Campaign from Geozone "timetable": { // optional "timezone": 1234, // in seconds "Mon": [ // available days: Mon, Tue, Wed, Thu, Fri, Sat, Sun. Push sending { "start": "04:11", "stop": "12:00" } ], "Sun": [ { // one or two intervals "start": "01:11", "stop": "17:00" }, { "start": "18:01", "stop": "23:59" } ] } }] } } ``` ### Adding several geozones at once `geozones` takes an array, so one call can create a whole batch. The batch is validated as a whole before anything is written: if any entry is rejected, the call fails and no geozone from that request is created. The error names the offending entry by its position in the array, counted from zero: ```json { "status_code": 210, "status_message": "geozones[301]: range: range must be at least 50 meters" } ``` Fix that entry and send the request again. On success, `GeoZones` holds the new numeric IDs in the same order as the entries you sent. Batches larger than 500 entries are accepted and split into chunks internally. The whole array is still validated before the first write, but the write itself is not atomic across chunks: an entry can pass validation and still fail to be written, for example if the preset it names is deleted in between. In that case the call returns `200` with the IDs that were written plus an `Errors` array naming the entry that stopped the run, so nothing created is lost: ```json { "status_code": 200, "status_message": "OK", "response": { "GeoZones": [100016750, 100016751], "Errors": [{ "index": 2, "message": "preset not found" }] } } ``` `Errors` is absent when every entry was written, so a fully successful response is unchanged. A `GeoZones` array shorter than the array you sent always means some entries were not created. Caution Geozone names are not unique. Re-sending a batch that already succeeded creates a second copy of every geozone in it. ### Polygon geozones Send `polygon` instead of `lat`/`lng`/`range` to make the geozone a polygon. `polygon.vertices` is an ordered ring of `{lat, lng}` points describing the shape’s outline: ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", "application": "XXXXX-XXXXX", "geozones": [{ "name": "Downtown mall — ground floor", "cooldown": 60, "polygon": { "vertices": [ { "lat": 40.70087797, "lng": -73.931851387 }, { "lat": 40.70112456, "lng": -73.931602211 }, { "lat": 40.70095321, "lng": -73.930987654 }, { "lat": 40.70068912, "lng": -73.931233456 } ] }, "content": "Welcome! Enjoy 15% off your first purchase today." }] } } ``` `lat`, `lng`, and `range` are derived from the ring — the circle a device actually monitors is centered on the shape with a radius reaching its farthest vertex (minimum 50 m). Sending `polygon` together with `lat`/`lng`/`range` is rejected. You can send the ring open or closed — if the last vertex repeats the first, the server drops that closing duplicate before validating. Vertex validation, on the resulting ring: 3 to 100 distinct vertices. The ring is also rejected if its points are collinear, if its edges self-intersect, or if it crosses the antimeridian. ## updateGeoZone Updates Geozone properties. ```http POST https://api.pushwoosh.com/json/1.3/updateGeoZone ``` ### Request body parameters | Parameter | Type | Required | Description | | ------------- | --------- | :------: | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **geoZoneId** | `string` | Yes | [Geozone ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#geozone-id) from `/addGeoZone` request. | | **name** | `string` | No | New Geozone name. | | **cooldown** | `integer` | No | Cooldown to update, in seconds. | | **status** | `integer` | No | 0 - deactivated, 1 - activated. | | **content** | `string` | No | Content for Geozone push notification. Cannot be used with `presetCode`. | | **cluster** | `string` | No | New cluster name. Specify `null` to unbind cluster from Geozone. | | **campaign** | `string` | No | New campaign ID. Specify `null` to unbind Campaign from Geozone. If omitted, Campaign value won’t be changed. Has higher priority than a Campaign from a preset. | | **lat** | `number` | No | Geozone latitude. Cannot be combined with `polygon`. | | **lng** | `number` | No | Geozone longitude. Cannot be combined with `polygon`. | | **range** | `integer` | No | New range in meters. Cannot be combined with `polygon`. | | **polygon** | `object` | No | New ring of `{lat, lng}` vertices — replaces the shape and re-derives `lat`/`lng`/`range` from it. See [Polygon geozones](https://docs.pushwoosh.com/developer/api-reference/geozones/#polygon-geozones). Works both ways: send it on an existing circle geozone to turn it into a polygon, or send an empty ring (`{"vertices": []}`) on an existing polygon geozone to turn it back into a circle. Omit `polygon` entirely to leave the shape unchanged. Same vertex validation as `addGeoZone`. | | **timetable** | `object` | No | Geozone timetable. See more info below. | *** ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "geoZoneId": 100016750, // required, from /addGeoZone method "name": "new geozone name", // optional "cooldown": 222, // in seconds, optional "status": 0, // optional, 0 - deactivated, 1 - activated "presetCode": "BBBBB-AAAAA", // optional, cannot be used along with "content" "content": "new geozone content", // optional, cannot be used along with "presetCode" "cluster": "GEOZONE CLUSTER CODE", // optional. Specify null to unbind cluster from Geozone "campaign": "CAMPAIGN_CODE", // optional. Specify null to unbind Campaign from Geozone "lat": 10.56, // optional, geozone latitude "lng": 12.523, // optional, geozone longitude "range": 500, // optional, geozone range "timetable": { // optional "timezone": 1234, // in seconds "Mon": [ // available days: Mon, Tue, Wed, Thu, Fri, Sat, Sun. Push sending { "start": "04:11", "stop": "12:00" } ], "Sun": [ { // one or two intervals "start": "01:11", "stop": "17:00" }, { "start": "18:01", "stop": "23:59" } ] } } } ``` ## deleteGeoZone Removes Geozones from the app. ```http POST https://api.pushwoosh.com/json/1.3/deleteGeoZone ``` ### Request body parameters | Parameter | Type | Required | Description | | --------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------ | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **geozones** | `string` | Yes | Array of IDs or a [single ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#geozone-id) of a Geozone to remove. | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "application": "XXXXX-XXXXX", // required, Pushwoosh application code "geozones": [550, 526] // required, geozones IDs } } ``` ## addGeoZoneCluster Adds Geozone Cluster to the app. ```http POST https://api.pushwoosh.com/json/1.3/addGeoZoneCluster ``` ### Request body parameters | Parameter | Type | Required | Description | | --------------- | --------- | :------: | ------------------------------------------------------------------------------------------------------------ | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **name** | `string` | Yes | Cluster name. | | **cooldown** | `integer` | Yes | A delay before a single user can receive the same message from the Geozone Cluster, in seconds. | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "application": "XXXXX-XXXXX", // required, Pushwoosh application code "name": "Raccoon city", // required, cluster name "cooldown": 3210 // required, in seconds } } ``` ## deleteGeoZoneCluster Removes a Geozone Cluster from the app. ```http POST https://api.pushwoosh.com/json/1.3/deleteGeoZoneCluster ``` ### Request body parameters | Parameter | Type | Required | Description | | ------------------ | -------- | :------: | ------------------------------------------------------------------------------------------------------------ | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **geoZoneCluster** | `string` | Yes | ID of the Geozone cluster to remove. | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "application": "XXXXX-XXXXX", // required, Pushwoosh application code "geoZoneCluster": "EA1CE-69405" // required, cluster ID obtained from the /addGeoZoneCluster request } } ``` ## listGeoZones Retrieves a list of Geozones for the app. ```http POST https://api.pushwoosh.com/json/1.3/listGeoZones ``` ### Request body parameters | Parameter | Type | Required | Description | | --------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------ | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "application": "XXXXX-XXXXX" // required, Pushwoosh application code } } ``` ## listGeoZoneClusters Retrieves a list of Geozone clusters for the app. ```http POST https://api.pushwoosh.com/json/1.3/listGeoZoneClusters ``` ### Request body parameters | Parameter | Type | Required | Description | | --------------- | -------- | :------: | ------------------------------------------------------------------------------------------------------------ | | **auth** | `string` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application** | `string` | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | ### Request example ```json { "request": { "auth": "yxoPUlwqm............pIyEX4H", // required, API access token from Pushwoosh Control "application": "XXXXX-XXXXX" // required, Pushwoosh application code } } ``` --- # Get started with Pushwoosh API **URL:** https://docs.pushwoosh.com/developer/api-reference/getting-started-with-pushwoosh-api/ Learn how to get started with Pushwoosh API, including authentication, making your first request, and understanding API responses. --- In this post we’ll walk you through the initial steps to set up and use the Pushwoosh API effectively. If you’re new to Pushwoosh or haven’t explored the API before, this guide will help you get started on the right track. ## Step 1: Sign up for access To get started with the new Pushwoosh API, you need a Pushwoosh account. Sign up for one if you do not have it yet. ## Step 2. Retrieve your API Access Token The API Access Token is a unique identifier assigned to your Pushwoosh account, allowing you to manage your projects through the API. This token is essential for various actions such as account modifications, sending messages, and editing projects. By default, your Pushwoosh account has an API Access Token for the Pushwoosh Demo App which you can use for experimentation. [Learn how to get your API Access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) ## Step 3. Make your first API request Once you have your Pushwoosh account and your API Access Token, you’re ready to explore the capabilities of the Pushwoosh API. To get started, select the relevant API method that aligns with the specific task you intend to accomplish and send your first API request. You can find the full list of API methods available [here](https://docs.pushwoosh.com/developer/api-reference/api-methods-reference-index). It’s crucial to include the `auth` parameter in your request by pasting your API Access Token. This authentication process guarantees that your requests are securely authorized and processed by Pushwoosh’s servers. --- # Google Wallet API **URL:** https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/ Create, update, and manage Google Wallet passes programmatically through the Pushwoosh Google Wallet API. --- The Google Wallet API lets you create, update, list, and manage [Google Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) programmatically. It supports the same operations the [pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) in the Control Panel performs. Use it to issue loyalty cards, offers, gift cards, event tickets, flight boarding passes, transit tickets, and generic passes, and to push live updates to passes already saved on your users’ devices. Prerequisite: Google Wallet configuration Before you can create passes for an application, a Google Wallet **Issuer ID** must be configured for that application in the Pushwoosh Control Panel, and the Pushwoosh service account (or your own service account) must be invited to your issuer. Without them, `create` and `update` requests fail. See [Google Wallet pass configuration for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/). ## Base URL ```plaintext https://apple-passkit.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise. ## Authentication Every request must include an `Authorization` header with your [Pushwoosh API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/): ```plaintext Authorization: Token ``` The account that owns the token must own the application referenced by `applicationCode`. A request for an application that belongs to another account returns `403 Forbidden`. ## Conventions * **Field naming:** JSON fields use `lowerCamelCase` (for example, `serialNumber`, `hexBackgroundColor`, `logoUrl`). * **Unpopulated fields:** responses include all fields, even when empty or zero-valued. * **Identity:** the `serialNumber` is always assigned by the server when a pass is created. Any value you send on create is ignored. The full Google Wallet object id is `{issuerId}.{serialNumber}`. * **Images:** `logoUrl` and `heroImageUrl` are public HTTPS URLs to images Google fetches—not uploaded files. * **Pass style:** exactly one style object (`generic`, `offer`, `loyalty`, `eventTicket`, `giftCard`, `flight`, or `transit`) must be set on a pass. The style cannot change after creation. ### Error responses | HTTP status | Meaning | | :------------------------ | :--------------------------------------------------------- | | `400 Bad Request` | Invalid argument—a required field is missing or malformed. | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application does not belong to the caller’s account. | | `404 Not Found` | The pass, template, or application was not found. | | `503 Service Unavailable` | The service is at capacity or temporarily unavailable. | ## Endpoints | Method | Path | Description | | :------- | :------------------------------------------------------------ | :-------------------------------------------------- | | `POST` | `/api/google/pass/validate` | Validate a pass configuration | | `POST` | `/api/google/pass/create` | Create a new pass object and get a save link | | `POST` | `/api/google/pass/update/{serialNumber}` | Update an existing pass; Google delivers the change | | `GET` | `/api/google/pass/{applicationCode}/{serialNumber}/save-link` | Get an “Add to Google Wallet” save link | | `GET` | `/api/google/pass/{applicationCode}/{serialNumber}` | Get a single pass | | `GET` | `/api/google/passes` | List all passes for an application | | `POST` | `/api/google/pass/{applicationCode}/{serialNumber}/state` | Activate or invalidate a pass | | `DELETE` | `/api/google/pass/{applicationCode}/{serialNumber}` | Delete a pass | | `GET` | `/api/google/config` | Get the application’s Google Wallet configuration | | `GET` | `/api/google/templates` | List available pass templates | | `GET` | `/api/google/templates/{filename}` | Get a single template | ## Create a pass Creates the pass class and object in Google Wallet, then returns the server-assigned serial number, the full object id, and an “Add to Google Wallet” save link. `POST` `/api/google/pass/create` ### Request body | Parameter | Type | Required | Description | | :---------------- | :----- | :------- | :------------------------------------------------------------------------------------------------ | | `pass` | object | Yes | The [pass object](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-object) describing the pass. Exactly one style must be set. | | `userId` | string | Yes | The [Pushwoosh User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) the pass is issued to. | | `applicationCode` | string | Yes | The [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | ##### Request example ```json { "applicationCode": "XXXXX-XXXXX", "userId": "user-123", "pass": { "hexBackgroundColor": "#3c414c", "logoUrl": "https://cdn.acme.com/logo.png", "loyalty": { "programName": "Acme Rewards", "accountName": "Jane Doe", "accountId": "1234567890", "pointsLabel": "Points", "pointsBalance": "1200", "rewardsTier": "Gold" }, "barcode": { "format": "QR_CODE", "value": "1234567890" } } } ``` ### Response | Field | Type | Description | | :------------- | :----- | :--------------------------------------------------------------------- | | `serialNumber` | string | Server-assigned unique identity of the created pass. | | `objectId` | string | Full Google Wallet object id: `{issuerId}.{serialNumber}`. | | `saveLink` | string | ”Add to Google Wallet” link: `https://pay.google.com/gp/v/save/{jwt}`. | | `message` | string | Result message. | ##### Response example ```json { "serialNumber": "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "objectId": "XXXXXXXXXXXXXXX.XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX", "saveLink": "https://pay.google.com/gp/v/save/{jwt}", "message": "Pass created successfully" } ``` ## Validate a pass Checks a pass configuration against Google’s requirements without creating it. Useful before calling create. `POST` `/api/google/pass/validate` ### Request body | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :------------------------------------------- | | `pass` | object | Yes | The [pass object](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-object) to validate. | ### Response | Field | Type | Description | | :--------- | :--------------- | :------------------------------------ | | `valid` | boolean | Whether the pass passes validation. | | `errors` | array of strings | Blocking problems that must be fixed. | | `warnings` | array of strings | Non-blocking advisories. | ## Update a pass Patches the pass object with new content. Google then delivers the updated version to every device that saved the pass. Optionally sends an Android notification with the update. `POST` `/api/google/pass/update/{serialNumber}` ### Path parameters | Parameter | Type | Description | | :------------- | :----- | :---------------------------------------------------- | | `serialNumber` | string | The serial number returned when the pass was created. | ### Request body | Parameter | Type | Required | Description | | :---------------- | :------ | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `updates` | object | Yes | The [pass object](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-object) with the new content. The style cannot change. | | `applicationCode` | string | Yes | The [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `notifyMessage` | string | No | When non-empty, push an Android notification with this text to everyone who saved the pass. Empty means a silent update. | | `notifyOnUpdate` | boolean | No | Request a field-update notification. Only `loyalty`, `eventTicket`, and `flight` passes actually notify; other styles accept the flag but never send one. Notifications fire only within 3 hours of a relevant start time, and Google limits them to 3 notifications per pass per 24 hours. | Two ways to notify on update `notifyMessage` sends a custom Android notification to everyone who saved the pass and works for any pass style. `notifyOnUpdate` asks Google to send its own field-update notification, which only fires for `loyalty`, `eventTicket`, and `flight` passes. ### Response | Field | Type | Description | | :-------- | :------ | :---------------------------- | | `success` | boolean | Whether the update succeeded. | | `message` | string | Result message. | ## Get a save link Returns an “Add to Google Wallet” save link for an already created pass. The pass object must already exist (created via [Create a pass](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#create-a-pass)). `GET` `/api/google/pass/{applicationCode}/{serialNumber}/save-link` ### Response | Field | Type | Description | | :--------- | :----- | :---------------------------------------- | | `saveLink` | string | `https://pay.google.com/gp/v/save/{jwt}`. | Share the link or render it as a QR code Put the `saveLink` behind an “Add to Google Wallet” button, or render it as a QR code with any QR library. When a user opens it, Google prompts them to save the pass and registers their device for updates. ## Get a pass Returns a single stored pass, including its full pass object. `GET` `/api/google/pass/{applicationCode}/{serialNumber}` ### Response Returns `{ "pass": { ... } }`, a single [pass record](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-record-object). ## List passes Returns a paginated, sorted list of the passes stored for an application. `GET` `/api/google/passes?applicationCode=XXXXX-XXXXX&page=0&perPage=20` ### Query parameters | Parameter | Type | Required | Description | | :---------------- | :------ | :------- | :-------------------------------------------------------------------------------------------- | | `applicationCode` | string | Yes | The [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `orderBy` | string | No | Sort field: `UPDATED` (default) or `CREATED`. | | `orderDirection` | string | No | Sort direction: `DESC` (default, newest first) or `ASC`. | | `page` | integer | No | Zero-based page index. Defaults to `0`. | | `perPage` | integer | No | Page size. `0` or omitted uses the server default. | ### Response | Field | Type | Description | | :-------- | :--------------- | :----------------------------------------------------------- | | `passes` | array of objects | The current page of [pass records](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-record-object). | | `page` | integer | The returned page index. | | `perPage` | integer | The page size used for this response. | | `total` | integer | Total number of passes for the application across all pages. | ## Set pass state Activates or invalidates a pass. An invalidated (inactive) pass moves to the user’s **Expired passes** section in Google Wallet; the record is kept so it can be re-activated. `POST` `/api/google/pass/{applicationCode}/{serialNumber}/state` ### Request body | Parameter | Type | Required | Description | | :-------- | :------ | :------- | :--------------------------------------------------------------------- | | `active` | boolean | Yes | `true` sets the pass to `ACTIVE`; `false` invalidates it (`INACTIVE`). | ### Response Returns an empty object `{}` on success. ## Delete a pass Invalidates the pass in Google and removes its stored record in Pushwoosh. `DELETE` `/api/google/pass/{applicationCode}/{serialNumber}` A saved pass cannot be force-removed Google does not allow removing a pass already saved on a user’s device. Delete invalidates the pass (it moves to **Expired passes**) and drops the Pushwoosh record. ### Response Returns an empty object `{}` on success. ## Get configuration Returns the Google Wallet configuration state for an application. `GET` `/api/google/config?applicationCode=XXXXX-XXXXX` ### Response | Field | Type | Description | | :----------------------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hasServiceAccount` | boolean | Whether Google Wallet is configured for the application, through the Pushwoosh service account or your own key. | | `issuerId` | string | The configured [Google Pay & Wallet Console Issuer ID](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/#create-the-issuer-account). | | `serviceAccountEmail` | string | The service account that issues passes for the application: the `client_email` of [your own key](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/#use-your-own-service-account-key-optional) if one is uploaded, otherwise the Pushwoosh service account. | | `pushwooshServiceAccountEmail` | string | The [Pushwoosh service account](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/#invite-the-pushwoosh-service-account) to invite to your issuer. | | `issuerStatus` | string | Whether passes can be issued into the issuer. `GOOGLE_ISSUER_STATUS_OK`: passes can be issued. `GOOGLE_ISSUER_STATUS_NOT_INVITED`: the service account is not among the issuer’s users yet. `GOOGLE_ISSUER_STATUS_NOT_OWNED`: none of the issuer’s users is a user of your Pushwoosh account. `GOOGLE_ISSUER_STATUS_UNAVAILABLE`: Google did not respond, retry later. | ## Templates List the available example pass templates, or fetch one as a [pass object](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-object) you can use as a starting point. `GET` `/api/google/templates` — returns `{ "templates": [ { "filename", "name", "description", "style" } ] }`. `GET` `/api/google/templates/{filename}` — returns `{ "template": { ...pass object... } }`. ## Object reference ### Pass object | Field | Type | Description | | :---------------------------------------------------------------------------------- | :----- | :----------------------------------------------------------------------------------------------------- | | `serialNumber` | string | Assigned by the server on create; identifies the pass. | | `generic` / `offer` / `loyalty` / `eventTicket` / `giftCard` / `flight` / `transit` | object | The pass style. **Exactly one** must be set. See the style objects below. | | `hexBackgroundColor` | string | Card background color, `#rrggbb`. | | `logoUrl` | string | Public HTTPS URL of the logo image. Required for loyalty and transit. | | `heroImageUrl` | string | Public HTTPS URL of a wide banner image. | | `barcode` | object | [Barcode](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#barcode-object) shown on the pass. | | `textModules` | array | [Text modules](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#text-module-object) shown in the details view. | | `links` | array | [Link modules](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#link-module-object) shown in the details view. | | `expirationTime` | string | ISO 8601 time when Google auto-expires the pass. Empty means no expiry. | | `appLink` | object | [App link](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#app-link-object): a CTA button on the front of the pass. | | `locations` | array | [Locations](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#location-object) that trigger a geofenced notification (max 10). | | `holdersPolicy` | string | Who may save the pass: `ONE_USER_ALL_DEVICES` (default), `ONE_USER_ONE_DEVICE`, or `MULTIPLE_HOLDERS`. | ### Generic object | Field | Type | Description | | :----------- | :----- | :----------------------------------------------------------------------------------- | | `cardTitle` | string | Required. The issuer/program name at the top of the card. | | `header` | string | Required. The main title of the card. | | `subheader` | string | Secondary title. | | `cardFields` | array | Up to 6 [text modules](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#text-module-object) pinned to the front (up to 3 rows of 2). | ### Offer object | Field | Type | Description | | :------------------ | :----- | :--------------------------------------------------------------------- | | `title` | string | Required. For example, `20% off everything`. | | `provider` | string | Required. The merchant name. | | `details` | string | Offer details. | | `finePrint` | string | Terms and conditions. | | `redemptionChannel` | string | `ONLINE`, `INSTORE`, `BOTH` (default), or `TEMPORARY_PRICE_REDUCTION`. | | `issuerName` | string | Shown on Google’s “issued by” surfaces; defaults to `provider`. | ### Loyalty object | Field | Type | Description | | :----------------- | :----- | :------------------------------------------------ | | `programName` | string | Required. Requires `logoUrl` on the pass. | | `accountName` | string | Member name shown on the card. | | `accountId` | string | Member id shown on the card. | | `pointsLabel` | string | For example, `Points`. Shown only with a balance. | | `pointsBalance` | string | The points balance. | | `rewardsTier` | string | For example, `Gold`. | | `rewardsTierLabel` | string | Label next to the tier; defaults to `Tier`. | | `issuerName` | string | Defaults to `programName`. | ### Event ticket object | Field | Type | Description | | :------------------------------------------------- | :----- | :--------------------------------------------------------------- | | `eventName` | string | Required. | | `venueName` / `venueAddress` | string | Venue details. | | `startDateTime` / `endDateTime` | string | ISO 8601 with offset (for example, `2026-07-01T19:30:00+02:00`). | | `ticketHolderName` / `ticketNumber` / `ticketType` | string | Holder and ticket details. | | `section` / `row` / `seat` / `gate` | string | Seating details. | | `issuerName` | string | Defaults to `eventName`. | ### Gift card object | Field | Type | Description | | :---------------- | :----- | :--------------------------------------------------------------- | | `merchantName` | string | Required. | | `cardNumber` | string | Required. | | `pin` | string | Card PIN. | | `balance` | string | Decimal amount, for example `25.00`. Requires `balanceCurrency`. | | `balanceCurrency` | string | ISO 4217 currency code, for example `USD`. | | `issuerName` | string | Defaults to `merchantName`. | ### Flight object | Field | Type | Description | | :---------------------------------------------------------------- | :----- | :--------------------------------------------------------------------------------------------------------- | | `carrierIataCode` | string | Required. 2-letter IATA code, for example `LX`. | | `airlineName` | string | Airline display name. | | `flightNumber` | string | Required. Digits only, for example `113`. | | `originAirportCode` / `destinationAirportCode` | string | Required. 3-letter IATA codes. | | `originTerminal` / `originGate` / `destinationTerminal` | string | Terminal and gate details. | | `departureDateTime` | string | Required. Origin airport **local** time, ISO 8601 **without** offset (for example, `2026-09-01T06:30:00`). | | `boardingTime` / `arrivalDateTime` | string | Same local format. `arrivalDateTime` is destination local time. | | `passengerName` | string | Required. | | `confirmationCode` / `seatNumber` / `seatClass` / `boardingGroup` | string | Passenger details. | | `issuerName` | string | Defaults to `airlineName`, then the carrier code. | ### Transit object | Field | Type | Description | | :-------------------- | :----- | :--------------------------------------------------------------- | | `transitType` | string | Required. `BUS`, `RAIL`, `TRAM`, `FERRY`, or `OTHER`. | | `transitOperatorName` | string | Required. Requires `logoUrl` on the pass. | | `passengerName` | string | Required. | | `ticketNumber` | string | Ticket number. | | `tripType` | string | `ONE_WAY` (default) or `ROUND_TRIP`. | | `legs` | array | One or more [transit legs](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#transit-leg-object) in travel order. | | `issuerName` | string | Defaults to `transitOperatorName`. | ### Transit leg object | Field | Type | Description | | :-------------------------------------- | :----- | :--------------------------------------------------- | | `originName` / `destinationName` | string | Required. | | `departureDateTime` / `arrivalDateTime` | string | ISO 8601; offset optional (local time when omitted). | | `platform` / `coach` / `seat` | string | Boarding details. | | `fareName` | string | For example, `Anytime Single`. | ### Barcode object | Field | Type | Description | | :-------- | :----- | :------------------------------------------------------------------------------------------ | | `format` | string | `QR_CODE`, `PDF_417`, `AZTEC`, `CODE_128`, `EAN_13`, and other Google Wallet barcode types. | | `value` | string | Data encoded in the barcode. | | `altText` | string | Text shown below the barcode. | ### Text module object | Field | Type | Description | | :------- | :----- | :------------------------ | | `id` | string | Identifier of the module. | | `header` | string | Module heading. | | `body` | string | Module text. | ### Link module object | Field | Type | Description | | :------------ | :----- | :------------------------------------ | | `uri` | string | External link URL. | | `description` | string | Link label shown in the details view. | ### App link object | Field | Type | Description | | :------------------- | :----- | :---------------------------------------------------------------------------------------- | | `uri` | string | Web URL or deep-link target URI. | | `androidPackageName` | string | Optional. When set, opens the Android app. | | `description` | string | Internal description of the target URI (not a visible button label); defaults to the URI. | ### Location object | Field | Type | Description | | :---------- | :----- | :-------------------- | | `latitude` | number | `-90.0` to `+90.0`. | | `longitude` | number | `-180.0` to `+180.0`. | ### Pass record object Returned by list/get endpoints. | Field | Type | Description | | :------------------------ | :----- | :--------------------------------------------------------------------------------------------- | | `serialNumber` | string | Pass serial number. | | `objectId` | string | Full Google Wallet object id `{issuerId}.{serialNumber}`. | | `cardTitle` | string | Display title/header for the pass. | | `header` | string | Secondary display title. | | `userId` | string | [Pushwoosh User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) the pass was issued to. | | `createdAt` / `updatedAt` | string | Creation and last-update timestamps. | | `state` | string | `ACTIVE` or `INACTIVE`. | | `style` | string | `generic`, `offer`, `loyalty`, `eventTicket`, `giftCard`, `flight`, or `transit`. | | `pass` | object | The full [pass object](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#pass-object), for editing. | --- # iOS Live Activities API **URL:** https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/ Create, update, and manage iOS Live Activities using API methods for real-time activity notifications. --- > Apple documentation: > > * [About Live Activities](https://developer.apple.com/design/human-interface-guidelines/live-activities) > * [Updating and ending Live Activities with ActivityKit push notifications](https://developer.apple.com/documentation/activitykit/updating-and-ending-your-live-activity-with-activitykit-push-notifications) To let a Customer Journey Live Activity point build its content-state form from field names instead of a raw JSON editor, publish a schema for your `attributes-type` — see the [Live Activity Schemas API](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/). ## startLiveActivity `POST` `https://api.pushwoosh.com/json/1.3/startLiveActivity` Allows creating iOS Live Activities. ### Request body | Parameter | Type | Required/Optional | Description | | ------------- | ------ | ----------------- | ---------------------------------------------------------------------------------------------------------------- | | application | String | Required | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | auth | String | Required | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from the Pushwoosh Control Panel. | | notifications | Array | Required | JSON array of message parameters. See details in the Notifications table below. | #### Notifications Parameters used in the `notifications` array: | Parameter | Type | Required/Optional | Description | | ------------------------------ | ---------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | content | String | Required* | Alert body for the push that starts the Live Activity, and the fallback text shown on devices running iOS versions below 16.1. | | title | String | Required* | Alert title for the push that starts the Live Activity. | | live_activity | Object | Required | Live Activity data to create Live Activity in iOS. | | live_activity.content-state | Object | Required | Content for the Live Activity notification. | | live_activity.attributes-type | String | Required | The type of attributes used in the Live Activity. | | live_activity.attributes | Object | Required | Attributes for the Live Activity. | | live_activity_id | String | Required | A unique identifier for the Live Activity. Used to target this activity when calling `updateLiveActivity`. Must be unique per activity session. | | filter | String | Optional | The name of a Pushwoosh filter (segment). See [Segment / Filter name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-name). The Live Activity will be started on all devices matching this filter. | | devices | Array of Strings | Optional | A list of [device tokens](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token). The Live Activity will be started only on the specified devices. | | send_date | String | Optional | Schedules the push that starts the Live Activity for a specific date and time — works with either `filter` or `devices` targeting. Use format `YYYY-MM-DD HH:mm`, or `now` to start immediately (this is also the default when the parameter is omitted). Must be no more than 1 day in the past or 30 days in the future, otherwise the request is rejected with a validation error. | | timezone | String | Optional | The timezone used to interpret `send_date`. If omitted, `send_date` is interpreted in UTC. | | apns_priority | Integer | Optional | Controls the APNs delivery priority for this Live Activity push. Accepts `10` (high priority, delivered with the `apns-priority: 10` header for instant rendering on a locked screen) or `5` (low priority, delivered with `apns-priority: 5` to conserve device battery). Any other value is treated as `5`, with no validation error. Every Live Activity push defaults to priority 5 regardless of whether it carries alert content (`content`/`title`) — set `apns_priority: 10` explicitly to request high-priority delivery. See [Time Sensitive Push and delivery priority](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/#time-sensitive-push-and-delivery-priority) below. | > **Note:** `*` At least one of `content` or `title` must be non-empty. Pushwoosh rejects a start request where both are empty. Caution Remote start of a Live Activity via `startLiveActivity` requires **iOS 17.2+** on the device — earlier versions do not support the `pushToStartTokenUpdates` API that this endpoint relies on. See [Start Live Activity with a Remote push notification](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/#start-live-activity-with-a-remote-push-notification). When targeting a `filter` (segment), the scheduled send time is when Pushwoosh starts dispatching the push, not a guaranteed delivery instant — for large audiences, delivery to individual devices can lag behind `send_date` by a few minutes, since the send rate for Live Activity pushes can’t be customized with `send_rate`. Repeated push-to-start calls exhaust silently. After roughly ten starts in quick succession, Apple stops delivering the underlying push-to-start notification without any error, and `startLiveActivity` still returns 200. During campaign testing this looks like “the API works but nothing happens on the device.” Space out consecutive test starts. ### Request example * With filter ```json { "request": { "application": "XXXXX-XXXXX", "auth": "SECRET_API_TOKEN", "notifications": [ { "content": "Your order is being prepared", "title": "Food Delivery", "apns_priority": 10, "live_activity": { "event": "start", "title": "Order status", "content-state": { "status": "Third", "estimatedTime": "37 min", "emoji": "👨‍🍳" }, "attributes-type": "FoodDeliveryAttributes", "attributes": {} }, "live_activity_id": "FIRST_LIVE_ACTIVITY", "filter": "FILTER_NAME_1" } ] } } ``` * With devices ```json { "request": { "application": "XXXXX-XXXXX", "auth": "SECRET_API_TOKEN", "notifications": [ { "content": "Your order is being prepared", "title": "Food Delivery", "apns_priority": 10, "live_activity": { "event": "start", "title": "Order status", "content-state": { "status": "Third", "estimatedTime": "37 min", "emoji": "👨‍🍳" }, "attributes-type": "FoodDeliveryAttributes", "attributes": {} }, "live_activity_id": "SECOND_LIVE_ACTIVITY", "devices": ["first_third", "second_device"] } ] } } ``` * Scheduled start ```json { "request": { "application": "XXXXX-XXXXX", "auth": "SECRET_API_TOKEN", "notifications": [ { "content": "Your order is being prepared", "title": "Food Delivery", "live_activity": { "event": "start", "title": "Order status", "content-state": { "status": "Third", "estimatedTime": "37 min", "emoji": "👨‍🍳" }, "attributes-type": "FoodDeliveryAttributes", "attributes": {} }, "live_activity_id": "THIRD_LIVE_ACTIVITY", "filter": "FILTER_NAME_1", "send_date": "2026-06-16 16:00" } ] } } ``` ### Response example ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": [ "XXXXX-XXXXXXXX-XXXXXXXX" ] } } ``` > **Note:** > > Read [this article](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities) to learn more about working with Live Activities using the Pushwoosh iOS SDK. ## updateLiveActivity `POST` `https://api.pushwoosh.com/json/1.3/updateLiveActivity` Allows updating and ending iOS Live Activities ### Request body | Parameter | Type | Required/Optional | Description | | ------------- | ------ | ----------------- | ---------------------------------------------------------------------------------------------------------------- | | auth | String | Required | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from the Pushwoosh Control Panel. | | application | String | Required | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | notifications | Array | Required | JSON array of message parameters. See details in the Notifications table below. | #### Notifications Parameters used in the `notifications` array: | Parameter | Type | Required/Optional | Description | | ------------------------------ | ------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | live_activity | Object | Required | Live Activity data to update Live Activity in iOS. | | live_activity.event | String | Required | Specifies the event type. Use `"update"` to update the Live Activity or `"end"` to close it. | | live_activity.content-state | Object | Required | Object with key-value pairs used to pass data to Live Activity for updating its content. | | live_activity.dismissal-date | Integer | Optional | The time (in seconds) when the Live Activity should end. On an `end`, omit this to leave the card showing its final `content-state` until iOS retires it on its own — see the note below. Set a date in the past to remove the card as soon as this update arrives instead. | | live_activity_id | String | Required | The unique identifier of the Live Activity to update. Must match the `live_activity_id` used in **startLiveActivity**. The update will be delivered to all devices on which this activity was started. | | live_activity.relevance-score | Integer | Optional | Tells the iOS system which Live Activity has higher priority than others. Accepts values from 1 to infinity (values up to 100 are recommended). | | live_activity.stale-date | Integer | Optional | The time (in seconds) that represents the date at which a Live Activity becomes stale, or out of date. | | apns_priority | Integer | Optional | Controls the APNs delivery priority for this Live Activity push. Accepts `10` (high priority, delivered with the `apns-priority: 10` header for instant rendering on a locked screen) or `5` (low priority, delivered with `apns-priority: 5` to conserve device battery). Any other value is treated as `5`, with no validation error. Every Live Activity push defaults to priority 5 regardless of whether it carries alert content (`content`/`title`) — set `apns_priority: 10` explicitly to request high-priority delivery. See [Time Sensitive Push and delivery priority](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/#time-sensitive-push-and-delivery-priority) below. | | content | String | Optional | Alert body for this update. The common case is a content-state-only update, which sets none of content, title, or subtitle and carries no alert at all. | | title | String | Optional | Alert title for this update. Setting content, title, or subtitle triggers an alert and lets `ios_sound` play. With none of the three set, the update stays silent, which is the default for content-state-only updates. | | subtitle | String | Optional | Alert subtitle for this update. Same alert-triggering role as `content`/`title` above. | | ios_sound | String | Optional | Sound file name in the app’s main bundle. It rides inside `aps.alert` alongside `content`/`title`/`subtitle`, not the top-level `aps.sound`, which ActivityKit ignores for Live Activities, so it only plays when this update also sets at least one of those three. iOS also rate-limits Live Activity alerts on its own. The same payload has been observed to arrive with sound on one delivery and without it on the next, on both device and Simulator. | > **Note:** `relevance-score` only affects the display order among multiple active Live Activities on the same device — it does not affect delivery urgency. Use `apns_priority` to control how urgently an update is delivered. Note An `end` with no `dismissal-date` doesn’t remove the card. It stays on the Lock Screen showing whatever `content-state` this update sent, and Apple’s own Live Activity system retires it on its own within up to 4 hours. ### Request example ```json { "request": { "application": "XXXXX-XXXXX", "auth": "SECRET_API_TOKEN", "notifications": [ { "apns_priority": 10, "title": "Live Activity Update", "live_activity": { "event": "update", "content-state": { "status": "second 66", "estimatedTime": "66 min", "emoji": "👨‍" }, "relevance-score": 60 }, "live_activity_id": "FIRST_LIVE_ACTIVITY" } ] } } ``` ### Response example ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": [ "XXXXX-XXXXXXXX-XXXXXXXX" ] } } ``` > [Read this article](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/) to learn more about working with Live Activities using the Pushwoosh iOS SDK. ## Time Sensitive Push and delivery priority By default, Apple delivers Live Activity updates at low priority (`apns-priority: 5`) to conserve battery. When a device is locked, a low-priority update is processed in the background and only becomes visible on the Lock Screen once the user unlocks the device. On an already-unlocked device it still renders instantly. Use the `apns_priority` parameter described above to request high-priority (`apns-priority: 10`) delivery so the update renders on the Lock Screen immediately, without unlocking. Not related to the Time Sensitive Push toggle The **Time Sensitive Push** toggle in the Pushwoosh Control Panel sets the `interruption-level` for standard push notifications only. It has no effect on Live Activity delivery priority sent through this API — Live Activities use a separate delivery path, controlled exclusively by the `apns_priority` request parameter. Even with `apns_priority: 10` available, Apple caps how often it can be used. APNs high-priority budget Apple limits how many high-priority (`apns-priority: 10`) pushes an app can send per hour. For frequently updated activities (e.g. live scores), reserve `apns_priority: 10` for critical events and use `apns_priority: 5` or fewer updates for routine changes. Enabling `NSSupportsLiveActivitiesFrequentUpdates` in your app’s Info.plist (see the [iOS SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/#infoplist-configuration)) raises this budget for high-frequency use cases. ## Multiple activities per device You can start multiple Live Activities on the same device by calling `startLiveActivity` several times with different `live_activity_id` values. For example, if you start two activities: `FIRST_LIVE_ACTIVITY` with `filter: FILTER_NAME_1` and `SECOND_LIVE_ACTIVITY` with `filter: FILTER_NAME_2`, a device that matches both filters will have both activities running simultaneously. To update one of them, pass its `live_activity_id` to `updateLiveActivity`. The update is delivered to all devices where that activity was created. The other activity is not affected. The `relevance-score` parameter controls display priority when multiple Live Activities are active on the same device. If screen space is limited or activities are grouped, the activity with a higher value is shown with higher priority. --- # Kakao API **URL:** https://docs.pushwoosh.com/developer/api-reference/kakao/ Send personalized Kakao messages to your users using pre-approved templates via Pushwoosh API. --- The Kakao API documents `/createKakaoMessage`, a deprecated method for sending KakaoTalk messages. Each message uses a template that Kakao has approved. /createKakaoMessage is deprecated New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — pass `platforms: ["KAKAO"]` to `Notify` and use the `kakao` block inside `payload.content.localized_content`. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/#from-createkakaomessage). ## createKakaoMessage Deprecated Use this endpoint to send Kakao messages to users. `POST` `https://api.pushwoosh.com/json/1.3/createKakaoMessage` Note This endpoint is dedicated to Kakao messaging only. For multi-channel messaging, use [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/). ### Prerequisites Before using this endpoint, ensure: 1. **Kakao platform is configured**: Your Pushwoosh application must have Kakao credentials configured. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/kakao-configuration/) 2. **Templates are approved**: Kakao templates must be created and approved before they can be used. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) 3. **Devices are registered**: Devices must be registered with the `kakao:` prefix to be recognized as Kakao endpoints. ### Request body | Name | Required | Type | Description | | :-------------- | :------- | :----- | :----------------------------------------------------------------------------------------------------------- | | auth* | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | notifications* | Yes | array | Array of notification objects. See details below. | ### Notification parameters | Name | Required | Type | Description | | :------------------------ | :------------------------------------ | :------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | send_date* | Yes | string | Date and time to send the message. Use format `YYYY-MM-DD HH:MM:SS` (UTC) or `"now"` to send immediately. All times are interpreted as UTC. | | devices* | Required if `users` is not provided | array[string] | List of device tokens. Each token **must** be prefixed with `kakao:` (e.g., `"kakao:user_token"`). | | users* | Required if `devices` is not provided | array[string] | List of user IDs to target. | | template* | Yes | string | Kakao template name. Must be a pre-approved template. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) | | kakao_content_variables | No | object | Key-value pairs for template variable substitution. Keys must match variables defined in your Kakao template. Optional but allows dynamic personalization of your Kakao messages. | Important You must provide either `devices` or `users`. Do not leave both empty. #### Forbidden parameters The following parameters are not allowed for this endpoint and will result in a validation error: * `platforms`: The platform is automatically set to Kakao * `filter`: Device filtering not supported * `filter_code`: Filter codes not supported * `conditions`: Conditional targeting not supported ### Request example ```json { "request": { "auth": "your-api-access-token", // required. API access token from Pushwoosh Control Panel. "application": "XXXXX-XXXXX", // required. Pushwoosh application code. "notifications": [ { "send_date": "now", // required. YYYY-MM-DD HH:MM:SS (UTC) OR "now". "devices": ["kakao:user123@kakao.com", "kakao:device_abc"], // required if users is not provided. Device tokens with kakao: prefix. "users": ["user_001", "user_002"], // required if devices is not provided. User IDs to target. "template": "welcome_message", // required. Kakao template name (must be pre-approved). "kakao_content_variables": { // optional. Template variable substitution. "user_name": "John Doe", "order_number": "12345" } } ] } } ``` ### Response example * 200 ```json { "status_code": 200, "response": { "Messages": ["MESSAGE_ID_1"], "Warnings": [], "UnknownDevices": {}, "UnknownUsers": {}, "FailedDevices": {}, "UnknownPhoneNumbers": {} } } ``` | Field | Type | Description | | --------------------- | -------------- | ----------------------------------------- | | `Messages` | array[string] | Array of message IDs created for tracking | | `Warnings` | array | Any warnings generated during processing | | `UnknownDevices` | object | Devices that could not be found | | `UnknownUsers` | object | User IDs that could not be resolved | | `FailedDevices` | object | Devices that failed during processing | | `UnknownPhoneNumbers` | object | Phone numbers that could not be found | * 210 ```json { "status_code": 210, "status_message": "Error description" } ``` ##### Common error messages | Error Message | Cause | | :--------------------------------------------- | :-------------------------------------------------------- | | `Missing required parameter: send_date` | The `send_date` field is not provided in the notification | | `Missing required parameter: devices or users` | Neither `devices` nor `users` array is provided | | `Invalid Kakao devices list` | One or more device tokens are missing the `kakao:` prefix | | `Invalid parameter: platforms` | Attempted to set platforms manually (not allowed) | | `Kakao template is required` | Template name was not provided | | `Invalid Kakao template` | Specified template does not exist | | `Kakao template not approved` | Template exists but is not approved by Kakao | | `Please configure Kakao platform` | Application does not have Kakao credentials configured | * 500 ```json { "status_code": 500, "status_message": "Internal server error" } ``` ### Code examples * cURL ```bash curl -X POST "https://api.pushwoosh.com/json/1.3/createKakaoMessage" \ -H "Content-Type: application/json" \ -d '{ "request": { "auth": "your-api-access-token", "application": "XXXXX-XXXXX", "notifications": [ { "send_date": "now", "devices": ["kakao:user123@kakao.com", "kakao:device_abc"], "template": "welcome_message", "kakao_content_variables": { "user_name": "John Doe", "order_number": "12345" } } ] } }' ``` * PHP ```php [ 'auth' => 'your-api-access-token', 'application' => 'XXXXX-XXXXX', 'notifications' => [ [ 'send_date' => 'now', 'devices' => ['kakao:user123@kakao.com', 'kakao:device_abc'], 'template' => 'welcome_message', 'kakao_content_variables' => [ 'user_name' => 'John Doe', 'order_number' => '12345' ] ] ] ] ]; $options = [ 'http' => [ 'header' => "Content-Type: application/json\r\n", 'method' => 'POST', 'content' => json_encode($data) ] ]; $context = stream_context_create($options); $result = file_get_contents($url, false, $context); echo $result; ``` * Python ```python import requests url = "https://api.pushwoosh.com/json/1.3/createKakaoMessage" payload = { "request": { "auth": "your-api-access-token", "application": "XXXXX-XXXXX", "notifications": [ { "send_date": "now", "devices": ["kakao:user123@kakao.com", "kakao:device_abc"], "template": "welcome_message", "kakao_content_variables": { "user_name": "John Doe", "order_number": "12345" } } ] } } response = requests.post(url, json=payload) print(response.json()) ``` ### Example: Sending to users instead of devices ```json { "request": { "auth": "your-api-access-token", "application": "XXXXX-XXXXX", "notifications": [ { "send_date": "now", "users": ["user_001", "user_002", "user_003"], "template": "promotion_alert", "kakao_content_variables": { "discount_percent": "20", "promo_code": "SAVE20" } } ] } } ``` ### Example: Scheduled message ```json { "request": { "auth": "your-api-access-token", "application": "XXXXX-XXXXX", "notifications": [ { "send_date": "2024-12-25 09:00:00", "devices": ["kakao:user123"], "template": "holiday_greeting" } ] } } ``` --- # LINE API **URL:** https://docs.pushwoosh.com/developer/api-reference/line-api/ Send personalized LINE messages via Pushwoosh API. Support for text, images, carousels, and Flex templates. --- /createLineMessage is deprecated New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — pass `platforms: ["LINE"]` to `Notify` and use the `line` block inside `payload.content.localized_content`. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/#from-createlinemessage). For rich image / carousel / flex messages, pre-configure them as LINE templates in your Pushwoosh Control Panel and reference the template code through `line.template`. Note Before sending LINE messages, ensure that the Line platform is properly configured. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/) ## createLineMessage Deprecated Used to send LINE messages to users `POST` `https://api.pushwoosh.com/json/1.3/createLineMessage` ### Send a textual message Simple LINE messages that consist only of plain text, without images or buttons. [Learn more](https://developers.line.biz/en/reference/messaging-api/#text-message) > **Tip:** For advanced formatting and rich media, use message templates like [Flex](https://docs.pushwoosh.com/developer/api-reference/line-api/#send-a-flex-message), [image](https://docs.pushwoosh.com/developer/api-reference/line-api/#send-an-image-message), or [carousel](https://docs.pushwoosh.com/developer/api-reference/line-api/#send-an-image-carousel-message). ##### Request body | Parameter | Type | Required | Description | | :-------------- | :--------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `auth` | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) for authenticating the request. | | `notifications` | array of objects | Yes | List of LINE message objects to be sent. | | `content` | string | Yes | Text of the LINE message to be sent. Max character limit: 5000. **Note:** If both `preset` and `content` are included, the value from the request overrides the `preset`. | | `preset` | string | No | Code of the [LINE preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) you created in the Pushwoosh Control Panel. **Note:** If both `preset` and `content` are included, the value from the request overrides the `preset`. | | `send_date` | string | Yes | Date and time to send the message. Use format `YYYY-MM-DD HH:mm` or `now` to send immediately. | | `devices` | array of strings | Yes | List of device codes (user IDs) to send the LINE message to. | ```plaintext { "request": { "application": "XXXXXX-XXXXXX", "auth": "**************************************", "notifications": [ { "content": "test", "preset": "preset_code", "send_date":"now", "devices": ["XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"] } ] } } ``` ### Send an image message You can send an [image message](https://developers.line.biz/en/reference/messaging-api/#image-message) to LINE users by specifying an image object within the template field of your request. Each image message requires two image URLs: one for the **full-size image (image_url)** and another for the **preview (preview_image_url)**. When users tap the preview, the full-size image opens. Make sure both URLs use HTTPS (TLS 1.2 or later) and link to publicly accessible image files. For details, see the [Image Message section](https://developers.line.biz/en/reference/messaging-api/#image-message) in the LINE Messaging API documentation. ##### Request body | Parameter | Type | Required | Description | | :-------------- | ---------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `auth` | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) used to authenticate the request. | | `notifications` | array of objects | Yes | List of messages to send. | | `content` | string | Yes | Used as fallback or preview text for the message. Code of the LINE preset you created in the Pushwoosh Control Panel. **Note:** If both `preset` and `template` are included, the `template` from the request is used. If both `preset` and `content` are included, the `content` from the request overrides the preset. | | `send_date` | string | Yes | Date and time to send the message. Use format `YYYY-MM-DD HH:mm` or `now` to send immediately. | | `devices` | array of strings | Yes | List of device codes (user IDs) to send the LINE message to. | | `preset` | string | No | Code of the [LINE preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) you created in the Pushwoosh Control Panel. **Note:** If both `preset` and `template` parameters are included in the request, the values from the `template` will override those defined in the preset. If both `preset` and `content` are included in the same request, the `content` provided directly in the request will override the content from the `preset`. | | `template` | object | Yes | Message layout template. Supports multiple message types. See details below. | ##### Template parameters **Type:** image | Parameter | Type | Required | Description | | :------------------ | :----- | :------- | :----------------------------------------------------------------------------------------------------------------------------------- | | `image_url` | string | Yes | URL to the full-size image (must use HTTPS). **Max length:** 2000 characters. **Format:** JPEG, PNG. **Max size:** 10 MB. | | `preview_image_url` | string | Yes | URL to the preview image shown in chat (must use HTTPS). **Max length:** 2000 characters. **Format:** JPEG, PNG. **Max size:** 1 MB. | ##### Example request ```json { "request": { "application": "XXXXXX-XXXXXX", "auth": "**************************************", "notifications": [ { "content": "test", "send_date": "now", "devices": [ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ], "preset": "preset_code", "template": { "en": { "image": { "alt_text": "some text", "image_url": "https://images.com/1.jpg", "preview_image_url": "https://images.com/1.png" } } } } ] } } ``` ### Send an image carousel message Image carousel messages let you display multiple images in a horizontally scrollable format. Each image appears as a separate, non-clickable column that users can swipe through in the LINE chat interface. This format is ideal for showcasing items, promotions, or visual selections in an engaging way. For details, refer to the [Image Carousel Template](https://developers.line.biz/en/reference/messaging-api/#carousel) section in the LINE Messaging API documentation. ##### Request body | Parameter | Type | Required | Description | | :-------------- | :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `auth` | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) used to authenticate the request. | | `notifications` | array of objects | Yes | List of messages to send. | | `content` | string | Yes | Used as fallback or preview text for the message. **Note:** If both `content` and `template` are set, the `template` is used. If both a `preset` and `content` are included in the same request, the `content` provided directly in the request will override the content from the `preset`. | | `send_date` | string | Yes | Date and time to send the message. Use the format `YYYY-MM-DD HH:mm` or `"now"`. | | `devices` | array of strings | Yes | List of device codes (user IDs) to send the LINE message to. | | `preset` | string | No | Code of the [LINE preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) you created in the Pushwoosh Control Panel. **Note:** If both `preset` and `template` parameters are included in the request, the values from the `template` will override those defined in the `preset`. If both a `preset` and `content` are included in the same request, the `content` provided directly in the request will override the content from the `preset`. | | `template` | object | Yes | Message layout template. Supports multiple message types. See details below. | ##### Template parameters **Type:** image_carousel | Parameter | Type | Required | Description | | :---------- | :--------------- | :------- | :----------------------------------------------------------------------------------------------------------------- | | `alt_text` | string | Yes | Fallback text displayed in push previews and on unsupported devices. Max 400 characters. | | `columns` | array of objects | Yes | Array of image columns (1–10 supported). Each column includes an image. | | `image_url` | string | Yes | URL to the image shown in each carousel column pointing to a publicly accessible JPEG or PNG file. Must use HTTPS. | ##### Example request ```json { "request": { "application": "XXXXXX-XXXXXX", "auth": "**************************************", "notifications": [ { "content": "test", "send_date": "now", "devices": [ "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX" ], "preset": "preset_code", "template": { "en": { "image_carousel": { "alt_text": "some text", "columns": [ { "image_url": "https://images.com/1.jpg" }, { "image_url": "https://images.com/2.jpg" }, { "image_url": "https://images.com/3.jpg" } ] } } } } ] } } ``` ### Send a Flex message Flex Messages are powerful, customizable message types that allow you to create structured layouts using text, images, buttons, and other components. They are ideal for use cases such as receipts, product cards, menus, or any content that benefits from visual structuring. To send a Flex Message, include a `template` object with a `raw` payload inside your request. The message layout must follow the [LINE Flex Message specification](https://developers.line.biz/en/docs/messaging-api/using-flex-messages/). **Tip:** You can design and preview Flex Messages using the [LINE Flex Message Simulator](https://developers.line.biz/flex-simulator/). ##### Request body | Parameter | Type | Required | Description | | :-------------- | :--------------- | :------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `auth` | string | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) used to authenticate the request. | | `notifications` | array of objects | Yes | List of messages to send. | | `content` | string | Yes | Used as fallback or preview text for the message. **Note:** If both `content` and `template` are set, the template is used. If both `preset` and `content` are included, the content from the request overrides the preset. | | `send_date` | string | Yes | When to send the message. Use `"now"` or `YYYY-MM-DD HH:mm` format. | | `devices` | array of strings | Yes | List of LINE device tokens (user IDs) to receive the message. | | `preset` | string | No | Code of the [LINE preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) you created in the Pushwoosh Control Panel. **Note:** If both `preset` and `template` are included, the template overrides the preset. If both `preset` and `content` are included, the content from the request overrides the preset. | | `template` | object | Yes | Message layout template. Supports multiple message types. See details below. | ##### Template parameters For a Flex Message, use the raw structure.\ Type: raw (Flex) | Parameter | Type | Required | Description | | :--------- | :----- | :------- | :-------------------------------------------------------------------------------------------------------------------- | | `alt_text` | string | Yes | Fallback text shown in notifications, chat previews, and quotes. Max 400 characters. | | `content` | object | Yes | Flex Message layout, structured using `bubble`, `box`, `text`, and other components as per LINE’s Flex specification. | ##### Example request ```json { "request": { "application": "XXXXXX-XXXXXX", "auth": "**************************************", "notifications": [ { "content": "test", "send_date": "now", "devices": ["XXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"], "preset": "preset_code", "template": { "en": { "raw": { "alt_text": "My raw template", "content": { "type": "bubble", "body": { "type": "box", "layout": "vertical", "contents": [ { "type": "text", "text": "RECEIPT", "weight": "bold", "color": "#1DB446", "size": "sm" } // Additional components... ] } } } } } } ] } } ``` --- # Live Activity Schemas API **URL:** https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/ Publish, list, retrieve, and delete the JSON Schema for a Live Activity widget's content and attributes, used by a journey point. --- A Live Activity schema is a JSON Schema for one `ActivityAttributes` type in your app (for example `FlightAttributes`), covering both halves of the card: the `ContentState` fields that change while the activity runs, and the fields fixed for its whole lifetime. Publish a schema so a journey’s [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) can build named fields for both halves from it, instead of a raw JSON editor and a free-form field list. The card and its layout are still built in your app’s code. The schema only describes the data a journey fills in. This API is for developers integrating Live Activities. See the [iOS Live Activities API](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/) for starting and updating activities themselves. ## Writing a schema `attributesType` is the name of the Swift type that conforms to `ActivityAttributes` in your app. Pushwoosh doesn’t read your code or validate the name against it — it’s just the string the API stores and the string you pass to `startLiveActivity`’s `attributes-type` field. `jsonSchema` covers both halves of that type, in two separate places: * The `ContentState` fields, the ones that change while the activity runs, like a flight’s gate, status, or ETA, go in the schema’s root `properties`. * The `ActivityAttributes` fields, fixed for the activity’s whole lifetime and set once when it starts, like a flight number, go in a separate `attributes` section, with its own `properties` and an optional `required` list. The `attributes` section is optional. Without it, `ActivityAttributes` fields stay a free field name/value list on the Live Activity element instead of named fields. You always pass the actual attribute values through `live_activity.attributes` on [startLiveActivity](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/#startliveactivity) — the schema only declares their names, types, and which ones are required. ### Example ```swift struct FlightAttributes: ActivityAttributes { struct ContentState: Codable, Hashable { var gate: String var status: String var estimatedTime: String } var flightNumber: String } ``` `flightNumber` lives in `ActivityAttributes`. `gate`, `status`, and `estimatedTime` live in `ContentState`. Publish both halves as the schema for `attributesType: "FlightAttributes"`: ```json { "type": "object", "properties": { "gate": { "type": "string" }, "status": { "type": "string" }, "estimatedTime": { "type": "string" } }, "attributes": { "properties": { "flightNumber": { "type": "string" } }, "required": ["flightNumber"] } } ``` `required` inside `attributes` makes `flightNumber` mandatory on the Live Activity element’s **Start** step: leaving it blank there is rejected. The root `properties` for `ContentState` fields has no such list. A journey is never required to fill `gate`, `status`, or `estimatedTime`. Once published, a journey’s [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) reads this shape to offer named fields for `gate`, `status`, and `estimatedTime` in **Card content**, instead of a raw content-state editor. The same happens for `flightNumber` in **Card attributes**, instead of a free field name/value list. See [Conventions](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#conventions) below for the format and immutability rules that apply to `jsonSchema`, and [Managing schemas in the Control Panel](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#managing-schemas-in-the-control-panel) at the end of this page for the same actions without calling the API directly. ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` ## Conventions * **Field naming is asymmetric.** Requests accept both `lowerCamelCase` and the proto name. Responses always come back with the proto field names, in `snake_case` (`attributes_type`, `json_schema`) — the examples below use that casing. * **Versions are immutable.** A published version can’t be edited — there is no `Update` method. Publishing again with the same `attributesType` and `version` fails with `AlreadyExists`. A widget change is always a new version. Omit `version` on `Create` to publish the next free one for that `attributesType`. * **`jsonSchema` format:** must be a JSON object with `"type": "object"`, up to 64 KB. `null`, a number, a bare string, or an object missing `"type": "object"` are all rejected, because the form Pushwoosh builds needs named fields, which only an object schema has. The optional `attributes` section, when present, must itself be an object with its own `properties` and, optionally, a `required` array that only names fields declared in `attributes.properties`. A field name can’t appear in both `properties` and `attributes.properties`. ## Endpoints | Method | Path | Description | | :------- | :------------------------------------------------------ | :---------------------------- | | `GET` | `/api/live_activity_schemas` | List an application’s schemas | | `GET` | `/api/live_activity_schemas/{attributesType}/{version}` | Get one schema version | | `POST` | `/api/live_activity_schemas` | Publish a new schema version | | `DELETE` | `/api/live_activity_schemas/{attributesType}/{version}` | Delete a schema version | ## List Lists every `attributesType` an application has published schemas for, with all their versions, newest version first. `GET` `/api/live_activity_schemas` ### Query parameters | Parameter | Type | Required | Description | | :--------------- | :----- | :------- | :------------------------------------------------------------------------------------------------------ | | `application` | string | Yes | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) to list schemas for. | | `attributesType` | string | No | Restrict the list to one `ActivityAttributes` type. | ##### Response example ```json { "schemas": [ { "application": "XXXXX-XXXXX", "attributes_type": "FlightAttributes", "version": 2, "json_schema": "{\"type\":\"object\",\"properties\":{\"gate\":{\"type\":\"string\"}},\"attributes\":{\"properties\":{\"flightNumber\":{\"type\":\"string\"}},\"required\":[\"flightNumber\"]}}", "created": "2026-09-01T10:00:00Z", "updated": "2026-09-01T10:00:00Z" }, { "application": "XXXXX-XXXXX", "attributes_type": "FlightAttributes", "version": 1, "json_schema": "{\"type\":\"object\",\"properties\":{\"gate\":{\"type\":\"string\"},\"status\":{\"type\":\"string\"}}}", "created": "2026-08-15T10:00:00Z", "updated": "2026-08-15T10:00:00Z" } ] } ``` ## Get Returns one schema version. `GET` `/api/live_activity_schemas/{attributesType}/{version}` ### Path parameters | Parameter | Type | Required | Description | | :--------------- | :------ | :------- | :------------------------------------- | | `attributesType` | string | Yes | Name of the `ActivityAttributes` type. | | `version` | integer | Yes | Schema version. | ### Query parameters | Parameter | Type | Required | Description | | :------------ | :----- | :------- | :------------------------------------------ | | `application` | string | Yes | The application code the schema belongs to. | ### Response Returns `{ "schema": { ... } }`, the schema object shown in [List](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#list) above. ## Create Publishes a new schema version for an `attributesType`. Returns the created schema, including the version it was assigned. `POST` `/api/live_activity_schemas` ### Request body | Parameter | Type | Required | Description | | :--------------- | :------ | :------- | :---------------------------------------------------------------------------------------------------- | | `application` | string | Yes | The application code to publish the schema in. | | `attributesType` | string | Yes | Name of the `ActivityAttributes` type declared in your app. | | `jsonSchema` | string | Yes | JSON Schema of both `ContentState` and `attributes`. See [Writing a schema](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#writing-a-schema) above. | | `version` | integer | No | Version to publish. Omit to get the next free version for this `attributesType`. | ##### Request example ```json { "application": "XXXXX-XXXXX", "attributesType": "FlightAttributes", "jsonSchema": "{\"type\":\"object\",\"properties\":{\"gate\":{\"type\":\"string\"},\"status\":{\"type\":\"string\"}},\"attributes\":{\"properties\":{\"flightNumber\":{\"type\":\"string\"}},\"required\":[\"flightNumber\"]}}" } ``` ### Response Returns `{ "schema": { ... } }`, the created schema object. Caution Publishing an `attributesType`/`version` pair that already exists fails with `AlreadyExists` — versions are immutable, so there’s no way to overwrite one. Publish without `version` to get the next one instead. ## Delete Permanently deletes one schema version. `DELETE` `/api/live_activity_schemas/{attributesType}/{version}` ### Path parameters | Parameter | Type | Required | Description | | :--------------- | :------ | :------- | :------------------------------------- | | `attributesType` | string | Yes | Name of the `ActivityAttributes` type. | | `version` | integer | Yes | Schema version to delete. | ### Query parameters | Parameter | Type | Required | Description | | :------------ | :----- | :------- | :------------------------------------------ | | `application` | string | Yes | The application code the schema belongs to. | ### Response Returns an empty object on success. Danger This cannot be undone. A journey’s [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) built against the deleted version keeps its already-saved content and attribute values. It just loses the form hints for both from then on: content falls back to a raw JSON editor, and attributes fall back to a free field name/value list. ### Error responses | HTTP status | Meaning | | :-------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `400 Bad Request` | Invalid argument: a required field is missing, `jsonSchema` fails the format rule above (including an invalid `attributes` section), or `jsonSchema` exceeds 64 KB. | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application does not belong to the caller’s account. | | `404 Not Found` | The application, or the `attributesType`/`version` pair, was not found. | | `409 Conflict` | `Create` was called with an `attributesType`/`version` pair that already exists (`AlreadyExists` on the wire). | | `500 Internal Server Error` | Unexpected server-side failure. | ## Managing schemas in the Control Panel The Control Panel offers the same actions as the API without calling it directly: list versions by type, publish a new version, view a version’s JSON, and delete a version (with a confirmation, since deletion is permanent). See [iOS Live Activity schemas configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-live-activity-schemas-configuration/) for the click path. --- # Message inbox API **URL:** https://docs.pushwoosh.com/developer/api-reference/message-inbox/ Retrieve and manage inbox messages using API methods to access message history and inbox status for users. --- The Message inbox API reads and updates the inbox messages of a user. Use `/getInboxMessages` to get the user’s messages and `/inboxStatus` to change the status of a message, for example when you build your own inbox UI. Both methods identify the user by [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id). ## getInboxMessages Returns the list of Inbox messages for a specified HWID. `POST` `https://api.pushwoosh.com/json/1.3/getInboxMessages` #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request Body | Name | Type | Description | | ------------- | ------- | ------------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | userId* | string | If no custom [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) is set, use HWID. | | hwid | string | [Hardware ID of a device](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) to get the Inbox messages for. | | last_code | string | Code of the last message retrieved in the previous response. If empty, the first messages are returned. | | count | integer | The number of messages to be showed in a single response; used for pagination. 0 for loading all messages in Inbox. | * 200 ```json { "response": { "messages": [{ // array of messages currently stored in user's Inbox "inbox_id": "avde1c792cd", "rt": "146078676", // date to remove the message from Inbox "send_date": "1459758676", // date when the message was saved to Inbox "order": "59138176", // position in Inbox presentation order "title": "New Rich Media in inbox!", "text": "Hi! New feature is available right now!", "action_type": "RichMedia", "action_params": { // payload describing the actions on push opening "rm": "{\"url\":\"https:\/\/richmedia.pushwoosh.com\/C82F7-51FC1.zip\",\"ts\":\"1459758676\",\"tags\":{\"tagName\":\"tagValue\"}}" }, "status": 1, // 1 - delivered, 2 - read, 3 - opened, // 4 - deleted(by a user), 5 - deleted from Control Panel "hash":"1C" // hash code of push notification }, { ... }], "next":"afarew511", // the next Inbox code to be returned by // the following /getInboxMessages request "deleted": ["bbaf85ab", "a41957b"], // the list of messages deleted from Control Panel "new_inbox": 6 // the number of delivered Inbox messages for that user (can be used for badge) } } ``` Example Request ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh app code "userId": "user_name_1", // required. User ID registered in Pushwoosh "device_type": 1 // optional. For device types refer to /registerDevice } } ``` Note For iOS, please make sure that you have added the Notification Service Extension to your project for tracking push delivery.[ Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-message-delivery-tracking/) ## inboxStatus Updates the status of a particular Inbox message. `POST` `https://api.pushwoosh.com/json/1.3/inboxStatus` #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request Body | Name | Type | Description | | ------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | inbox_code* | string | could be obtained from Message details in Control Panel or from the “order” parameter of the `/geInboxMessages` request | | hash | string | Hash code of Inbox message. | | hwid | string | [Hardware ID of the device](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) to update Inbox status for. | | time | string | Send date timestamp. | | userID* | string | If no custom [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) is set, use HWID. | | status* | integer | 1 - delivered, 2 - read, 3 - opened, 4 - deleted by a user. | | device_type | integer | [See possible values](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type). | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "inbox_code": "874393934032909", // required. "order" parameter of /getInboxMessages request "hash": "1C", // optional. "userId": "some_user", // required. "status": 2, // required. status to set: 1-delivered, 2-read, 3-opened, 4-deleted (by a user) "device_type": 3 // optional. } } ``` --- # Messages API **URL:** https://docs.pushwoosh.com/developer/api-reference/messages-api/ Create, delete, cancel, and manage push notifications using Messages API methods for targeted messaging. --- /createMessage and /createTargetedMessage are deprecated New integrations should use the unified [**Messaging API v2**](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — one endpoint replaces the entire `/create*Message` family. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) for a field-by-field mapping. The legacy methods below remain fully operational. `/deleteMessage`, `/cancelMessage`, and `/getMessageDetails` are not deprecated — use them as usual against v1- or v2-created messages, except that `/getMessageDetails` can’t find [a message sent now to fewer than 10 recipients](https://docs.pushwoosh.com/developer/api-reference/messages-api/#messages-sent-now-to-fewer-than-10-recipients). Note To get started, please check out the descriptions of the [/createMessage request parameters](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites). ## createMessage Deprecated `POST` `https://api.pushwoosh.com/json/1.3/createMessage` Creates a new push notification. #### Request Body | Name | Type | Description | | --------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | notifications* | array | JSON array of message parameters. See details in a request example below. | Tip Pass an `X-Request-ID` header to correlate the created message with your request for end-to-end tracing — see [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id). * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": [ "C3F8-C3863ED4-334AD4F1" ] } } ``` Note /createMessage method supports content templates. To learn more, please refer to the [Liquid Templates guide](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/). ### Request example Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code. "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "notifications": [{ "send_date": "now", // optional. YYYY-MM-DD HH:mm OR 'now' "content": { // optional. object OR string. "en": "English", // Use "wns_content" instead for Windows. "fr": "French" }, "title": { // optional. object OR string. "en": "Title", // Ignored if platform-specific titles are specified "fr": "Titre" // 'ios_title', 'android_header', etc. }, // see the platform-specific parameters examples below. "subtitle":{ // optional. object OR string. "en": "Subtitle", // Ignored if platform-specific titles are specified "fr": "Sous-titre" // 'ios_subtitle', etc. }, // see the platform-specific parameters examples below. "ignore_user_timezone": true, // optional. "timezone": "America/New_York", // optional. If ignored UTC-0 is default for "send_date". // See https://php.net/manual/timezones.php for // supported timezones. "campaign": "CAMPAIGN_CODE", // optional. Campaign code to which you want to // assign this push message. "campaign_name": "Black Friday", // optional. Internal name for this message, shown as // the row title in Message History and in its // export. Unrelated to "campaign" above. It // doesn't group messages or affect delivery. // Max 255 characters. Empty falls back to the // notification's own title/content. "geozone": { // optional. Send to Geozone "lat": 22.22, "lng": 33.33, "range": 110 }, "rich_media": "XXXXX-XXXXX", // optional. Copy the Rich Media code from the URL bar of // the Rich Media editor page in Pushwoosh Control Panel. "link": "https://google.com", // optional. For deeplinks add "minimize_link": 0 "minimize_link": 0, // optional. 0 — do not minimize, 2 — bitly. Default = 2. // Please note that shorteners have restrictions // on a number of calls. "data": { // optional. JSON string or JSON object, will be passed as "key": "value" // "u" parameter in the payload (converted to JSON string). }, "transactionId": "unique UUID", // optional. Unique message identifier to prevent duplicating // in case of network problems. Stored on the side of // Pushwoosh for 5 minutes. "platforms": [ // optional. 1 — iOS; 3 — Android; 7 — Mac OS X; 8 — Windows; 1, 3, 7, 8, 9, 10, // 9 — Amazon; 10 — Safari; 11 — Chrome; 11, 12, 17 // 12 — Firefox; 17 — Huawei ], "preset": "XXXXX-XXXXX", // optional. Push Preset Code from your Control Panel. // If specific params are sent in the request, // they override preset's params. "send_rate": 100, // optional. Throttling. Valid values are from 100 to 1000 pushes/second. "send_rate_avoid": true, // optional. If set to true, throttling limit will not be applied to // this specific push notification. // Templating related, please refer to the Template Engine guide to learn more "template_bindings": { // optional. "TemplatePlaceholder": "Value" }, "dynamic_content_placeholders": { // optional. Placeholders for dynamic content instead of device tags. "firstname": "John", "lastname": "Doe" }, "message_type": "marketing", // optional. "marketing" or "transactional". // If omitted, treated as marketing: not delivered to control group members. // Set "transactional" to deliver to everyone. // Frequency capping params. Ensure that Global frequency capping is configured in the Control Panel. // Frequency capping does not apply to transactional messages. // In all other cases, including omitted "message_type", frequency capping applies. "capping_days": 30, // optional. Amount of days for frequency capping (max 30 days) "capping_count": 10, // optional. The max number of pushes that can be sent from a // specific app to a particular device within a 'capping_days' // period. In case the message created exceeds the // 'capping_count' limit for a device, it won't // be sent to that device. "capping_exclude": true, // optional. If set to true, this push notification will not // be counted towards the capping for future pushes. "capping_avoid": true, // optional. If set to true, capping will not be applied to // this specific push notification. // To save the message to the Inbox via API, use "inbox_date" or "inbox_image". // The message is saved when at least one of these parameters is used. "inbox_date": "2017-02-02", // optional. Specify when to remove a message from the Inbox. // Message will be removed from Inbox at 00:00:01 UTC // of the date specified, so the previous date is the // last day a user can see the message in their Inbox. // If not specified, the default removal date is the // next day after the send date. "inbox_image": "Inbox image URL", // optional. The image to be shown near the message. "inbox_days": 5, // optional. Specify when to remove a message from the // Inbox(lifetime of an inbox message in days). // Can be used instead of the "inbox_date" parameter. // Up to 30 days. "devices": [ // optional. Specify tokens or hwids to send targeted push "hwid_XXXX" // notifications. Not more than 1000 tokens/hwids in ], // an array. If set, the message will only be sent to // the devices on the list. Application Group for devices // list is not allowed. iOS push tokens can only be lower case. "to": [ // optional. For email, SMS, and similar channels. List of recipients "email_1", "email_2" // (e.g. email addresses, phone numbers). Max 1000 items. ], // For push, use "devices" instead. // User-centric push notifications "users": [ // optional. If set, message will only be delivered to the "user_XXXX" // specified user ID's (set via /registerUser call). ], // If specified together with devices or to, // the latter will be ignored. Not more than 1000 user // ID's in an array. Application Group for users list // is not allowed. // Filters and conditions "filter": "FILTER_NAME", // optional. "conditions": [ // optional. See the remark below. ["Country", "EQ", "fr"], ["Language", "EQ", "en"] ], "conditions_operator": "AND" // optional. Logical operator for conditions arrays. // Possible values: AND | OR. AND is default. }] } } ``` ### VoIP notification request example Pushwoosh supports VoIP-style call notifications for iOS and Android.\ Below you can find example API `createMessage` requests for each platform. #### iOS Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code. "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "notifications": [ { "voip_push": true, // required. Parameter is required to send out a VoIP push notification. "ios_root_params": { "aps": { "mutable-content": 1 // required for iOS10+ Media attachments. }, "callerName": "CallerName", // optional. Caller name. If not specified, "unknown caller" is shown. "video": true, // optional. Indicates whether video calls are supported. "supportsHolding": true, // optional. Indicates whether call holding functionality is supported. "supportsDTMF": false, // optional. Controls Dual-Tone Multi-Frequency signal support. "callId": "42", // optional. The unique identifier of the call to cancel. "cancelCall": true // optional. Set to "true" to cancel the call with the specified "callId". } } ] } } ``` #### Android Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code. "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "notifications": [ { "voip_push": true, // required. Parameter is required to send out a VoIP push notification. "android_root_params": { "callerName": "callerName", // optional. Caller name. If not specified, "unknown caller" is shown. "video": true, // optional. Indicates whether video calls are supported. "callId": 42, // optional. The unique identifier of the call to cancel. "cancelCall": true // optional. Set to "true" to cancel the call with the specified "callId". } } ] } } ``` ### Platform-specific parameters #### iOS parameters Example ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "ios_title": { // optional. Object OR string. Adds iOS specific title for push notification. "en": "title" }, "ios_subtitle": { // optional. Object OR string. Adds iOS specific subtitle for push notification. "en": "subtitle" }, "ios_content": { // optional. Object OR string. Adds iOS specific content for push notification. "en": "content" }, "ios_badges": 5, // optional. iOS application badge number. // Use "+n" or "-n" to increment/decrement the badge value by n. "ios_sound": "sound file.wav", // optional. Sound file name in the main bundle of application. // If left empty, the device will produce a default system sound. "ios_sound_off": true, // optional. Enable/disable sound set by "ios_sound" field. "ios_ttl": 3600, // optional. Time to live parameter - maximum message lifespan in seconds. "ios_silent": 1, // optional. Enables silent notifications (ignore "sound" and "content"). "ios_category_id": "1", // optional. iOS8 category ID from Pushwoosh. "ios_root_params": { // optional. Root level parameters to the aps dictionary. "aps": { "content-available": "0", // optional. Set "1" to send a silent push and "0" for regular push. "mutable-content": 1 // required for iOS10+ Media attachments. }, "callerName": "CallerName", // optional VoIP parameter. Caller name. If not specified, "unknown caller" is shown. "video": true, // optional VoIP parameter. Indicates whether video calls are supported. "supportsHolding": true, // optional VoIP parameter. Indicates whether call holding functionality is supported. "supportsDTMF": false, // optional VoIP parameter. Controls Dual-Tone Multi-Frequency signal support. "data": {} // optional User supplied data, max of 4KB }, "ios_attachment": "URL", // optional. Insert media content in notification. "ios_thread_id": "some thread id", // optional. Identifier to group related notifications. // Messages with the same thread ID will be grouped // on the lock screen and in the Notification Center. "ios_critical": true, // optional. Marks iOS notification as a critical alert // playing sound even if a device is muted or // Do Not Disturb mode is on. "ios_category_custom": "category", // optional. Custom APNS category. "ios_interruption_level": "active", // optional. One of "passive", "active", "time-sensitive", // "critical". Indicates the importance and // delivery timing of a notification. Refer to the // One-time push guide for details. "apns_collapse_id": "promo", // optional. APNs collapse identifier. Notifications with the same // apns_collapse_id replace each other on the device. "apns_trim_content": 1 // optional. (0|1) Trims the exceeding content strings with ellipsis. }] } } ``` #### Android parameters Example ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "android_header": { // optional. Android notification header. "en": "header" }, "android_content": { // optional. Android notification content. "en": "content" }, "android_root_params": { // optional. Custom key-value object. "key": "value", // Root level parameters for the android payload recipients. "CancelID": 12345678, // optional. Cancels the push notification with the "voip": true, // required VoIP parameter. Parameter is required to send out VoIP push notifications. "callerName": "callerName", // optional VoIP parameter. Caller name. If not specified, "unknown caller" is shown. "video": true, // optional VoIP parameter. Indicates whether video calls are supported. }, // specified Message ID (get the ID from the Message History) "android_sound": "soundfile", // optional. No file extension. If left empty, // the device will produce a default system sound. "android_sound_off": true, // optional. Enable/disable sound set by "android_sound" field "android_icon": "icon.png", // optional. "android_custom_icon": "URL.png", // optional. Full URL to the image file. "android_banner": "URL.png", // optional. Full URL to the image file. "android_badges": 5, // optional. Android application icon badge number. // Use "+n" or "-n" to increment/decrement the badge value by n. "android_gcm_ttl": 3600, // optional. Time to live parameter — maximum message lifespan in seconds. "android_vibration": 0, // optional. Android force-vibration for high-priority pushes. "android_led": "#rrggbb", // optional. LED hex color, device will do its best approximation. "android_priority": -1, // optional. Sets the "importance" parameter for devices with // Android 8.0 and higher, as well as the "priority" parameter // for devices with Android 7.1 and lower. Establishes the // interruption level of a notification channel or a particular // notification. Valid values are -2, -1, 0, 1, 2. "android_delivery_priority": "normal", // optional. "normal" or "high". // Enables notification’s delivery when the // device is in the power saving mode. "android_ibc": "#RRGGBB", // optional. icon background color on Lollipop, #RRGGBB, // #AARRGGBB, "red", "black", "yellow", etc. "android_silent": 1, // optional. 0 or 1. Enable silent notification. // Ignore sound and content "android_group_id": "123", // optional. Identifier to group related notifications. Messages with // the same thread ID will be grouped in // the Notification Center. "android_collapse_key": "promo" // optional. FCM collapse key. Notifications with the same // collapse key replace each other while the device is offline. }] } } ``` **Huawei parameters** Huawei ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "huawei_android_header": { // optional. Object OR string. Notification title "en": "header" }, "huawei_android_content": { // optional. Object OR string. Notification content "en": "content" }, "huawei_android_badges": true, // optional. "huawei_android_silent": 0, // optional. 0 or 1. Enable silent notification. // Ignore sound and content "huawei_android_icon": "URL.png", // optional. "huawei_android_led": "#FF0011", // optional. LED hex color, device will do its best approximation "huawei_android_vibration": 1, // optional. Huawei force-vibration for high-priority pushes "huawei_android_sound": "sound.wav", // optional. If left empty, the device will produce // a default system sound "huawei_android_sound_off": true, // optional. Enable/disable sound set by // "huawei_android_sound" field "huawei_android_custom_icon": "URL.png", // optional "huawei_android_gcm_ttl": 2400, // optional. Time to live parameter - maximum // message lifespan in seconds "huawei_android_banner": "URL.png", // optional. Full path URL to the image file "huawei_android_root_params": { // optional. Custom key-value object. "key": "value" // Root-level parameters for Huawei payload recipients. }, "huawei_android_priority": 0, // optional. Valid values: -2, -1, 0, 1, 2 "huawei_android_ibc": "#0011AA", // optional. Icon background color on Lollipop "huawei_android_lockscreen": 1, // optional "huawei_android_delivery_priority": "normal", // optional. "normal" or "high". Enables notification // delivery in power saving mode "huawei_android_group_id": "group_id" // optional. Identifier to group related notifications }] } } ``` #### Safari parameters Safari ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "safari_url_args": [ // required, but the value may be empty "firstArgument", "secondArgument" ], "safari_title": { // optional. Object OR string. Title of the notification. "en": "content" }, "safari_content": { // optional. Object OR string. Content of the notification. "en": "content" }, "safari_action": "Click here", // optional. "safari_ttl": 3600 // optional. Time to live parameter — the maximum // lifespan of a message in seconds. }] } } ``` #### Chrome parameters Chrome ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "chrome_title": { // optional. Object OR string. You can specify the header "en": "title" // of the message in this parameter. }, "chrome_content": { // optional. Object OR string. You can specify the content "en": "content" // of the message in this parameter. }, "chrome_icon": "URL.png", // optional. Full URL to the icon or extension resources file path "chrome_gcm_ttl": 3600, // optional. Time to live parameter – maximum message lifespan in seconds. "chrome_duration": 20, // optional. max 50 seconds. Changes chrome push display time. // Set to 0 to display push until user interacts with it. "chrome_image": "image_URL", // optional. URL to large image. "chrome_root_params": { // optional. Set parameters specific to messages sent to Chrome. "key": "value" }, "chrome_button_text1": "text1", // optional "chrome_button_url1": "button1_URL", // optional. Ignored if chrome_button_text1 is not set. "chrome_button_text2": "text2", // optional "chrome_button_url2": "button2_url" // optional. Ignored if chrome_button_text2 is not set. }] } } ``` #### Firefox parameters Firefox ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "firefox_title": { // optional. Object OR string. You can specify message header here. "en": "title" }, "firefox_content": { // optional. Object OR string. You can specify message content here. "en": "content" }, "firefox_icon": "URL.png", // optional. Full path URL to the icon or path to the // file in extension resources. "firefox_root_params": { // optional. Set parameters specific to messages sent to Firefox. "key": "value" } }] } } ``` #### Amazon parameters Amazon ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "adm_header": { // optional. Object OR string. You can specify message header here. "en": "header" }, "adm_content": { // optional. Object OR string. You can specify message content here. "en": "content" }, "adm_root_params": { // optional. Custom key-value object "key": "value" }, "adm_sound": "push.mp3", // optional. "adm_sound_off": true, // optional. Enable/disable sound set by "adm_sound" field "adm_icon": "icon.png", // optional. Full URL to the icon. "adm_custom_icon": "URL.png", // optional. "adm_banner": "URL.png", // optional. "adm_ttl": 3600, // optional. Time to live parameter — the maximum message // lifespan in seconds. "adm_priority": -1 // optional. Priority of the push in Amazon push drawer, // valid values are -2, -1, 0, 1 and 2. }] } } ``` #### Mac OS X parameters Mac OS X ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "mac_title": { // optional. Object OR string. Adds Title for push notification. "en": "title" }, "mac_subtitle": { // optional. Adds subtitle for push notification. "en": "subtitle" }, "mac_content": { // optional. Adds content for push notification. "en": "content" }, "mac_badges": 3, // optional. "mac_sound": "sound.caf", // optional. "mac_sound_off": true, // optional. Enable/disable sound set by "mac_sound" field "mac_root_params": { // optional. "content-available": 1 }, "mac_ttl": 3600 // optional. Time to live parameter — maximum message lifespan in seconds. }] } } ``` #### Windows parameters Windows ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "notifications": [{ "wns_content": { // required. Content (XML or raw) of notification encoded in MIME's base64 // in form of Object OR String "en": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48YmFkZ2UgdmFsdWU9ImF2YWlsYWJsZSIvPg==", "de": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48YmFkZ2UgdmFsdWU9Im5ld01lc3NhZ2UiLz4=" }, "wns_type": "Badge", // optional. 'Tile' | 'Toast' | 'Badge' | 'Raw' "wns_tag": "myTag", // optional. Used in Tile replacement policy. // An alphanumeric string of no more than 16 characters. "wns_cache": 1, // optional. (1|0) Translates into X-WNS-Cache-Policy value. "wns_ttl": 600 // optional. Expiration time for notification in seconds. }] } } ``` Note Quick start! Check out these cool third-party libraries! Python library **by Pushwoosh**: Laravel library: Node JS client Meteor JS client Rails library Golang library **Response**: | HTTP Status code | status_code | Description | | ---------------- | ------------ | ------------------------------------------------- | | 200 | 200 | Message successfully created | | 200 | 210 | Argument error. See status_message for more info | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | Note An error in an array of notifications If the `createMessage` request has several messages in the `notifications` array, they will be processed and sent one by one. If one of the messages cannot be parsed, our API will return `"status_code":210` with the codes of successfully sent messages, i.e. those preceding the faulty message in the request. A `210` also covers an unapproved preset: if [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account and the `preset` isn’t **Approved**, the response carries `"status_message":"Preset content is not approved"`. ### API messaging tracing For load balancing purposes, *we do not store messages sent through API with the “devices” parameter that contains less than 10 devices in an array*. Due to this, such messages will not be displayed in your Message History. To see push reports during the testing phase, use **API messaging tracing**. Turning this option **ON** allows you to *override this limit for 1 hour and save such pushes in the Message History*. API messaging tracing turns OFF automatically after 1 hour. API messaging tracing can be activated on the [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) page by clicking **Start API messaging tracing** in the upper right corner. ### Tag conditions Each tag condition is an array like `[tagName, operator, operand]` where * tagName: name of a tag * operator: “EQ” | “IN” | “NOTEQ” | “NOTIN” | “LTE” | “GTE” | “BETWEEN” | “NOTSET” | “ANY” * operand: string | integer | array | date #### Operator description * EQ: tag value is equal to operand; * IN: tag value intersects with operand (operand must always be an array); * NOTEQ: tag value is not equal to an operand; * NOTIN: tag value does not intersect with operand (operand must always be an array); * GTE: tag value is greater than or equal to operand; * LTE: tag value is less than or equal to operand; * BETWEEN: tag value is greater than or equal to min operand value but less than or equal to max operand value (operand must always be an array); * NOTSET: tag not set. Operand is not considered; * ANY: tag has any value. Operand is not considered. #### String tags Valid operators: EQ, IN, NOTEQ, NOTIN, NOTSET, ANY\ Valid operands: * EQ, NOTEQ: operand must be a string; * IN, NOTIN: operand must be an array of strings like `["value 1", "value 2", "value N"]`; * NOTSET: tag not set. Operand is not considered; * ANY: tag has any value. Operand is not considered. #### Integer tags Valid operators: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE, NOTSET, ANY\ Valid operands: * EQ, NOTEQ, GTE, LTE: operand must be an integer; * IN, NOTIN: operand must be an array of integers like `[value 1, value 2, value N]`; * BETWEEN: operand must be an array of integers like `[min_value, max_value]`; * NOTSET: tag not set. Operand is not considered; * ANY: tag has any value. Operand is not considered. #### Date tags Valid operators: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE, NOTSET, ANY\ Valid operands: * `"YYYY-MM-DD 00:00"` (string) * unix timestamp `1234567890` (integer) * `"N days ago"` (string) for operators EQ, BETWEEN, GTE, LTE #### Boolean tags Valid operators: EQ, NOTSET, ANY\ Valid operands: `0, 1, true, false` #### List tags Valid operators: IN, NOTIN, NOTSET, ANY\ Valid operands: operand must be an array of strings like `["value 1", "value 2", "value N"]`. Danger Remember that “filter” and “conditions” parameters should not be used together.\ Also, both of them **will be ignored**, if the “devices” parameter is used in the same request. Note #### Country and Language tags Language tag value is a lowercase two-letter code according to [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes)\ Country tag value is an UPPERCASE two-letter code according to [ISO_3166-2](https://en.wikipedia.org/wiki/ISO_3166-2)\ For example, to send push a notification to Portuguese-speaking subscribers in Brazil, you will need to specify the following condition: `"conditions": [["Country", "EQ", "BR"],["Language", "EQ", "pt"]]` ### /createMessage snippets Important Please be careful when using the snippets. Limit the number of recipients by specifying “users”, “devices”, “filter”, or “conditions” parameter. If none of these parameters is specified, the message will be sent **to every device** subscribed to push notifications from the application. Sample `/createMessage` requests: * Bash ```bash #!/bin/bash #Usage if [ ! -n "$1" ] || [ ! -n "$2" ] then echo "`basename $0` usage: api_token appid message"; exit 1; fi; MESSAGE="$3"; if [ -z "$3" ] then MESSAGE='One push to rule them all!' fi; echo -e "Response:" curl --data-binary " {\"request\": {\"application\":\"$2\", \"auth\":\"$1\", \"notifications\": [{ \"send_date\": \"now\", \"content\": \"$MESSAGE\" }] } }" \ -H "Content-type: application/json" \ "https://api.pushwoosh.com/json/1.3/createMessage" echo ""; exit 0; ``` * PHP ```php $data]); $ch = curl_init($url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_ENCODING, 'gzip, deflate'); curl_setopt($ch, CURLOPT_HEADER, true); curl_setopt($ch, CURLOPT_POST, true); curl_setopt($ch, CURLOPT_POSTFIELDS, $request); $response = curl_exec($ch); $info = curl_getinfo($ch); curl_close($ch); if (defined('PW_DEBUG') && PW_DEBUG) { print "[PW] request: $request "; print "[PW] response: $response "; print '[PW] info: ' . print_r($info, true); } } pwCall('createMessage', array( 'application' => PW_APPLICATION, 'auth' => PW_AUTH, 'notifications' => array( array( 'send_date' => 'now', 'content' => 'test', 'data' => array('custom' => 'json data'), 'link' => 'https://pushwoosh.com/' ) ) ) ); ``` * Erlang ```erlang -module(pushwoosh). -export([run/0, stop/0, sendMessage/1]). %% sendMessage argument: message text %% %% Authentication & App_id %% -define(PW_AUTH, "YOUR_AUTH_TOKEN"). -define(PW_APPLICATION, "YOUR_PUSHWOOSH_APP_CODE"). %% KickStart %% run() -> application:start(unicode), application:start(crypto), application:start(public_key), application:start(ssl), application:start(inets), %% HTTP Client verbosity options flase, verbose, debug httpc:set_options([{verbose, false}]). stop() -> application:stop(ssl), application:stop(public_key), application:stop(crypto), application:stop(inets). %% JSON Wars ! encode(S) -> encode(S, [$"]). encode([], Acc) -> lists:reverse([$" | Acc]); encode([C | Cs], Acc) -> Hex = lists:flatten(io_lib:format("~4.16.0b", [C])), encode(Cs, lists:reverse(Hex) ++ "u\" ++ Acc). sendMessage(Message_text) -> %% URL to JSON API 1.3 Url = "https://api.pushwoosh.com/json/1.3/createMessage", EncodedMessage = encode(Message_text), {ok, Response} = httpc:request( %%Method post, %%Request {Url, [{"User-Agent", "Erlang exemple"}], "application/json; charset=UTF-8", "{\"request\":{ \"application\": \""?PW_APPLICATION"\", \"auth\": \""?PW_AUTH"\", \"notifications\": [{ \"send_date\": \"now\", \"content\": "++EncodedMessage++" }]}}"}, %%HTTP options [{ssl,[{verify, verify_none}]}, {version, "HTTP/1.0"}], %%Options []), io:format("And received ~p", [Response]). ``` * Ruby ```ruby class PushNotification #- PushWoosh API Documentation https://www.pushwoosh.com/programming-push-notification/pushwoosh-push-notification-remote-api/ #- Two methods here: # - PushNotification.new.notify_all(message) Notifies all with the same option # - PushNotification.new.notify_devices(notification_options = {}) Notifies specific devices with custom options include HTTParty #Make sure to have the HTTParty gem declared in your gemfile https://github.com/jnunemaker/httparty default_params :output => 'json' format :json def initialize #- Change to your settings @auth = {:application => "00000-00000",:auth => "auth_token"} end # PushNotification.new.notify_all("This is a test notification to all devices") def notify_all(message) notify_devices({:content => message}) end # PushNotification.new.notify_device({ # :content => "TEST", # :data => {:custom_data => value}, # :devices => array_of_tokens #}) def notify_devices(notification_options = {}) #- Default options, uncomment :data or :devices if needed default_notification_options = { # YYYY-MM-DD HH:mm OR 'now' :send_date => "now", # Object( language1: 'content1', language2: 'content2' ) OR string :content => { :fr => "Test", :en => "Test" }, # JSON string or JSON object "custom": "json data" #:data => { # :custom_data => value #}, # omit this field (push notification will be delivered to all the devices for the application), or provide the list of devices IDs #:devices => {} } #- Merging with specific options final_notification_options = default_notification_options.merge(notification_options) #- Constructing the final call options = @auth.merge({:notifications => [final_notification_options]}) options = {:request => options} #- Executing the POST API Call with HTTPARTY - :body => options.to_json allows us to send the json as an object instead of a string response = self.class.post("https://api.pushwoosh.com/json/1.3/createMessage", :body => options.to_json,:headers => { 'Content-Type' => 'application/json' }) end end ``` * Java ```java // Uses JSON classes from https://json.org/java/ package com.arellomobile; import org.json.*; import java.io.*; import java.net.*; public class SendPushNotificationSample { public static final String PUSHWOOSH_SERVICE_BASE_URL = "https://api.pushwoosh.com/json/1.3/"; private static final String AUTH_TOKEN = "YOUR_AUTH_TOKEN"; private static final String APPLICATION_CODE = "PW_APPLICATION_CODE"; public static void main(String[] args) throws JSONException, MalformedURLException { String method = "createMessage"; URL url = new URL(PUSHWOOSH_SERVICE_BASE_URL + method); JSONArray notificationsArray = new JSONArray() .put(new JSONObject().put("send_date", "now") .put("content", "test") .put("link", "https://pushwoosh.com/")); JSONObject requestObject = new JSONObject() .put("application", APPLICATION_CODE) .put("auth", AUTH_TOKEN) .put("notifications", notificationsArray); JSONObject mainRequest = new JSONObject().put("request", requestObject); JSONObject response = SendServerRequest.sendJSONRequest(url, mainRequest.toString()); System.out.println("Response is: " + response); } } class SendServerRequest { static JSONObject sendJSONRequest(URL url, String request) { HttpURLConnection connection = null; try { connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("POST"); connection.setRequestProperty("Content-Type", "application/json"); connection.setDoInput(true); connection.setDoOutput(true); DataOutputStream writer = new DataOutputStream(connection.getOutputStream()); writer.write(request.getBytes("UTF-8")); writer.flush(); writer.close(); return parseResponse(connection); } catch (Exception e) { System.out.println("An error occurred: " + e.getMessage()); return null; } finally { if (connection != null) { connection.disconnect(); } } } static JSONObject parseResponse(HttpURLConnection connection) throws IOException, JSONException { String line; BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); StringBuilder response = new StringBuilder(); while ((line = reader.readLine()) != null) { response.append(line).append(' '); } reader.close(); return new JSONObject(response.toString()); } } ``` * Python ```python import json PW_AUTH = 'API TOKEN' PW_APPLICATION_CODE = 'APPLICATION CODE' try: # For Python 3.0 and later from urllib.request import urlopen from urllib.request import Request except ImportError: # Fall back to Python 2's urllib2 from urllib2 import urlopen from urllib2 import Request def pw_call(method, data): url = 'https://api.pushwoosh.com/json/1.3/' + method data = json.dumps({'request': data}) req = Request(url, data.encode('UTF-8'), {'Content-Type': 'application/json'}) try: f = urlopen(req) response = f.read() f.close() print('Pushwoosh response: ' + str(response)) except Exception as e: print ('Request error: ' + str(e)) if __name__ == '__main__': pw_call('createMessage', { 'auth': PW_AUTH, 'application': PW_APPLICATION_CODE, 'notifications': [ { 'send_date': 'now', 'content': 'test', 'data': {"custom": "json data"}, 'link': 'https://pushwoosh.com' } ] } ) ``` * .NET ```plaintext using System; using System.IO; using System.Net; using Newtonsoft.Json.Linq; namespace WebApplication1 { public partial class Default : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { string pwAuth = "YOUR_AUTH_TOKEN"; string pwApplication = "PW_APPLICATION_CODE"; JObject json = new JObject( new JProperty("application", pwApplication), new JProperty("auth", pwAuth), new JProperty("notifications", new JArray( new JObject( new JProperty("send_date", "now"), new JProperty("content", "test"), new JProperty("wp_type", "Toast"), new JProperty("wp_count", 3), new JProperty("data", new JObject( new JProperty("custom", "json data"))), new JProperty("link", "https://pushwoosh.com/"), new JProperty("conditions", new JArray( (object)new JArray("Color", "EQ", "black"))))))); PWCall("createMessage", json); } private void PWCall(string action, JObject data) { Uri url = new Uri("https://api.pushwoosh.com/json/1.3/" + action); JObject json = new JObject(new JProperty("request", data)); DoPostRequest(url, json); } private void DoPostRequest(Uri url, JObject data) { HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(url); req.ContentType = "text/json"; req.Method = "POST"; using (var streamWriter = new StreamWriter(req.GetRequestStream())) { streamWriter.Write(data.ToString()); } HttpWebResponse httpResponse; try { httpResponse = (HttpWebResponse)req.GetResponse(); } catch (Exception exc) { throw new Exception(string.Format("Problem with {0}, {1}", url, exc.Message)); } using (var streamReader = new StreamReader(httpResponse.GetResponseStream())) { var responseText = streamReader.ReadToEnd(); Page.Response.Write(responseText); } } } } ``` * Go ```go package main import ( "fmt" "encoding/json" "net/http" "bytes" "io/ioutil" ) const ( PW_APPLICATION = "APPLICATION CODE" PW_AUTH = "API TOKEN" PW_ENDPOINT = "https://api.pushwoosh.com/json/1.3/" ) func pwCall(method string, data []byte) (bool) { url := PW_ENDPOINT + method request, err := http.NewRequest("POST", url, bytes.NewBuffer(data)) request.Header.Set("Content-Type", "application/json") client := http.Client{} response, err := client.Do(request) if err != nil { fmt.Println("Error occur: " + err.Error()) return false } defer response.Body.Close() fmt.Println("Response Status: ", response.Status) if (response.StatusCode == 200) { body, _ := ioutil.ReadAll(response.Body) fmt.Println("Response Body: ", string(body)) return true } return false } func main() { requestData := map[string]interface{}{ "request": map[string]interface{} { "auth": PW_AUTH, "application": PW_APPLICATION, "notifications": []interface{}{ map[string]interface{} { "send_date": "now", "content": "test", "link": "https://pushwoosh.com", }, }, }, } jsonRequest, _ := json.Marshal(requestData) requestString := string(jsonRequest) fmt.Println("Request body: " + requestString) pwCall("createMessage", jsonRequest) } ``` * JavaScript ```javascript $.ajax({ type: "POST", url: "https://api.pushwoosh.com/json/1.3/createMessage", data: JSON.stringify({ "request": { "application": "APPLICATION CODE", "auth": "API TOKEN", "notifications": [{ "send_date": "now", "ignore_user_timezone": true, "content": "Hello world!" }] } }), dataType: "json" }).done(function(data) { console.log(data); }); ``` ## deleteMessage `POST` `https://api.pushwoosh.com/json/1.3/deleteMessage` Deletes a message, whether it’s still scheduled or has already been sent. For a scheduled message, this also removes it from Message History right away — for an already-sent message, see the caution below. Caution Deleting an already-sent message still succeeds, but Message History may keep showing it under its last status (`done`, `canceled`, `fail`) instead of marking it deleted. The message itself is deleted for good, with no way to access it. Searching Message History for its message code or ID, or opening its Message stats, no longer works. Follow-up sends created from the message with [Resend to non-openers](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) are not affected — delete or cancel each of them separately. Use `/cancelMessage` instead if you want to stop the sending but keep the message in Message History with the `canceled` status. #### Request Body | Name | Type | Description | | --------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | message* | string | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained in `/createMessage` request. | * 200 ```json { "status_code": 200, "status_message": "OK" } ``` Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "message": "xxxx-xxxxxxx-xxxxxx" // required. Message code obtained in /createMessage } } ``` Note The method works for messages in the `pending`, `waiting`, `processing`, `done`, `canceled`, or `fail` status. It’s not allowed while a message is in the `creating` status — that returns a `210` error. **Status codes:** | HTTP Status code | status_code | Description | | ---------------- | ------------ | ----------------------------------------------------------------------------------------------------- | | 200 | 200 | Message successfully deleted | | 200 | 210 | Argument error, e.g. the message is still in the `creating` status. See status_message for more info | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | ```php setMessage('MESSAGE_CODE'); // call '/deleteMessage' Web Service $response = $pushwoosh->deleteMessage($request); if($response->isOk()) { print 'Great, my message has been deleted !'; } else { print 'Oups, the deletion failed :-('; print 'Status code : ' . $response->getStatusCode(); print 'Status message : ' . $response->getStatusMessage(); } ``` ## getMessageDetails `POST` `https://api.pushwoosh.com/json/1.3/getMessageDetails` Retrieves the message details. #### Request Body | Name | Type | Description | | --------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | message* | string | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) or message ID. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "message": { "id": 2068991743, "created": "2016-09-14 17:19:42", "send_date": "2016-09-14 17:19:41", "status": "done", "content": { "en": "Hello {Name|CapitalizeFirst|friend}! 🚀" }, "platforms": "[1]", "ignore_user_timezone": "1", "code": "XXXX-92B4C3C5-A7F5EF70", "data": { "key": "value" } } } } ``` Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "message": "xxxx-xxxxxxx-xxxxxx" // required. message code or message ID } } ``` ### Messages sent now to fewer than 10 recipients `/getMessageDetails` returns `Message not found` for a message sent now (without a future `send_date`) to an explicit list of fewer than 10 users and fewer than 10 devices, because such a message isn’t stored: ```json { "status_code": 210, "status_message": "Message not found" } ``` This applies to `/createMessage` with `devices` or `users` and to [`/messaging/v2/notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `transactional`. Devices are counted after users are resolved to their devices. The message is still delivered, and its `message_code` is returned in the response. `/getMsgStats` returns the same error for this code, and `/getMessageLog` filtered by this code returns no events. A message sent now is stored, and `/getMessageDetails` finds it, in any of these cases: * It targets 10 or more users or 10 or more devices. * It targets a segment or a filter. * It targets test devices rather than a `devices` list. * It is sent to all subscribers. * The account is on a trial. A message scheduled for a future `send_date` is stored regardless of the recipient count. Live Activity and Live Update messages are the exception: outside a trial, they aren’t stored whatever the targeting or send date. To check delivery of a message that isn’t stored, call [`/getMessageLog`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog) with these parameters: * `application_code` * `hwid` or `user_id` of the recipient * `date_from` and `date_to` covering the send time * `message_type` set to `"transactional"` ## createTargetedMessage Deprecated `POST` `https://api.pushwoosh.com/json/1.3/createTargetedMessage` Creates a new targeted push notification. #### Request Body | Name | Type | Description | | ---------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | devices_filter* | string | See remark below. | | send_date* | string | YYYY-MM-DD HH:mm or ‘now’. | | ignore_user_timezone | boolean | If ignored, UTC-0 is default for “send_date”. | | timezone | string | If ignored, UTC-0 is default for “send_date”. | | campaign | string | [Code of a campaign](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to which you want to assign this push message. | | campaign_name | string | Internal name for this message, shown as the row title in Message History and in its export. Unrelated to `campaign` above. It doesn’t group messages or affect delivery. Max 255 characters. Empty falls back to the notification’s own title/content. `createTargetedMessage` forwards its request body to `createMessage` unchanged, so this field works exactly the same as [there](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage). | | content* | string | Notification content. See the request example for details. | | transactionId | string | Unique message identifier to prevent duplicating messages in case of network problems. Stored on the side of Pushwoosh for 5 minutes. | | link | string | Link to be opened once a user opens a push message. | | minimize_link | integer | 0 - do not minimize, 2 - bit.ly. Default = 2. | | data | object | JSON string or JSON object. Will be passed as “u” parameter in the payload (converted to JSON string). | | preset | string | [Preset code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#preset-code). | | send_rate | integer | Throttling. Valid values are from 100 to 1000 pushes per second. | | inbox_date | string | Specify when to remove a message from the Inbox. | | inbox_image | string | URL of the image to be shown near the message in the Inbox. | Tip Pass an `X-Request-ID` header to correlate the created message with your request for end-to-end tracing — see [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id). * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "messageCode": "97B0-C7473871-2FBDFDC6" } } ``` * 400 JSON syntax errors ```plaintext The request cannot be fulfilled due to bad syntax. ``` More response examples: * 210 - syntax ```json { "status_code": 210, "status_message": "Errors occurred while compiling filter", "response": { "errors": [{ "message": "Invalid tag set specification. \")\" expected.", "type": "syntax" }] } } ``` * 210 - semantic ```json { "status_code": 210, "status_message": "Errors occurred while compiling filter", "response": { "errors": [{ "message": "Application \"11111-11111\" not found", "type": "semantic", "near": "\"11111-11111\"" }] } } ``` * 210 - lexical ```json { "status_code": 210, "status_message": "Errors occurred while compiling filter", "response": { "errors": [{ "message": "Invalid character \"/\" at 1:19", "type": "lexical" }] } } ``` Hard mode Should you be using [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) instead? * Example Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "devices_filter": "A(\"XXXXX-XXXXX\") * T(\"City\", EQ, \"Name\")", // required. Syntax explained below "send_date": "now", // optional. YYYY-MM-DD HH:mm OR 'now' "ignore_user_timezone": true, // optional. "timezone": "America/New_York", // optional. If ignored UTC-0 is default for "send_date". // More info https://php.net/manual/timezones.php. "campaign": "CAMPAIGN_CODE", // optional. Campaign code to which you want to assign this push message. "campaign_name": "Black Friday", // optional. Internal name for this message. Same field as // "campaign_name" on /createMessage. "content": { // optional. Object OR string. Use "wns_content" instead for Windows. "en": "English", "de": "Deutsch" }, "transactionId": "unique UUID", // optional. Unique message identifier to prevent duplicating messages // in case of network problems. Stored on the side of // Pushwoosh for 5 minutes. "rich_media": "XXXXX-XXXXX", // optional. Copy the Rich Media code from the URL bar of the // Rich Media editor page in Pushwoosh Control Panel. "link": "https://google.com", // optional. For deeplinks add "minimize_link": 0 "minimize_link": 0, // optional. 0 — do not minimize, 2 — bitly. Default = 2. // Google URL shortener is disabled since March 30, 2019. // Please note that shorteners have restrictions // on a number of calls. "data": { // optional. JSON string or JSON object. "key": "value" // Will be passed as "u" parameter in the payload }, // (converted to JSON string). "preset": "XXXXX-XXXXX", // optional. Push Preset Code from your Control Panel. "send_rate": 100, // optional. Throttling. Valid values are from 100 to 1000 pushes/second. "dynamic_content_placeholders": { // optional. Placeholders for dynamic content instead of device tags. "firstname": "John", "lastname": "Doe" }, // To save the message to the Inbox via API, use "inbox_date" or "inbox_image". // The message is saved when at least one of these parameters is used. "inbox_image": "Inbox image URL", // optional. The image to be shown near the message. "inbox_date": "2017-02-02" // optional. Specify when to remove a message from the Inbox. // Message will be removed from Inbox at 00:00:01 UTC of // the date specified, so the previous date is the last // day a user can see the message in their Inbox. // If not specified, the default removal date is the next // day after the send date. } } ``` * Platform-specific parameters Platform-specific parameters ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "devices_filter": "FILTER CONDITION", "send_date": "now", // optional. YYYY-MM-DD HH:mm OR 'now' "content": { // optional. Object OR string. "en": "English", // Use "wns_content" instead for Windows. "de": "Deutsch" }, "ignore_user_timezone": true, // optional. "timezone": "America/New_York", // optional. If ignored UTC-0 is default for "send_date". // More info https://php.net/manual/timezones.php. "campaign": "CAMPAIGN_CODE", // optional. Campaign code to which you want to assign this push message. "campaign_name": "Black Friday", // optional. Internal name for this message. Same field as // "campaign_name" on /createMessage. // iOS related parameters "ios_badges": 5, // optional. iOS application badge number. // Use "+n" or "-n" to increment/decrement the badge value by n. "ios_sound": "sound file.wav", // optional. Sound file name in the main bundle of application. // If left empty, the device will produce no sound // upon receiving a push. "ios_sound_off": true, // optional. Enable/disable sound set by "ios_sound" field. "ios_ttl": 3600, // optional. Time to live parameter — maximum message lifespan in seconds. "ios_silent": 1, // optional. Enables silent notifications (ignore "sound" and "content"). "ios_category_id": "1", // optional. iOS8 category ID from Pushwoosh. "ios_category_custom": "category", // optional. Custom APNS category. "ios_root_params": { // optional. Root level parameters to the aps dictionary. "aps": { "content-available": "0", // optional. Set "1" to send a silent push and "0" for regular push. "mutable-content": 1 // required for iOS10+ Media attachments. }, "attachment": "YOUR_ATTACHMENT_URL", // iOS10+ media attachment URL. "data": {} // optional. User supplied data, max of 4KB }, "apns_trim_content": 1, // optional. (0|1) Trims the exceeding content strings with ellipsis. "ios_title": { // optional. Adds title for iOS push notification. "en": "title" }, "ios_subtitle": { // optional. Adds subtitle for iOS push notification. "en": "subTitle" }, "ios_content": { // optional. Adds content for iOS push notification. "en": "content" }, // Android related parameters "android_root_params": { // optional. Custom key-value object. "key": "value" // Root level parameters for the android payload recipients. }, "android_sound": "soundfile", // optional. No file extension. If left empty, the device // will produce no sound upon receiving a push. "android_sound_off": true, // optional. Enable/disable sound set by "android_sound" field "android_header": { // optional. Object OR string. Android notification header. "en": "header" }, "android_content": { // optional. Object OR string. Android notification content. "en": "content" }, "android_icon": "icon.png", "android_custom_icon": "URL.png", // optional. Full path URL to the image file. "android_banner": "URL.png", // optional. Full path URL to the image file. "android_badges": 5, // optional. integer. Android application icon badge number. // Use "+n" or "-n" to increment/decrement the badge value by n. "android_gcm_ttl": 3600, // optional. Time to live parameter — maximum message lifespan in seconds. "android_vibration": 0, // optional. Android force-vibration for high-priority pushes. "android_led": "#rrggbb", // optional. LED hex color, device will do its best approximation. "android_priority": -1, // optional. Sets the "importance" parameter for devices with Android 8.0 // and higher, as well as the "priority" parameter for devices // with Android 7.1 and lower. Establishes the interruption // level of a notification channel or a particular notification. // Valid values are -2, -1, 0, 1, 2. "android_delivery_priority": "normal", // optional. "normal" or "high". Enables notification’s delivery // when the device is in the power saving mode. "android_ibc": "#RRGGBB", // optional. icon background color on Lollipop, #RRGGBB, // #AARRGGBB, "red", "black", "yellow", etc. "android_silent": 1, // optional. 0 or 1. Enable silent notificaiton. // Ignore sound and content // Amazon related parameters "adm_root_params": { // optional. Custom key-value object "key": "value" }, "adm_sound": "push.mp3", "adm_sound_off": true, // optional. Enable/disable sound set by "adm_sound" field "adm_header": { "en": "Header" }, "adm_content": { "en": "content" }, "adm_icon": "icon.png", "adm_custom_icon": "URL.png", "adm_banner": "URL.png", "adm_ttl": 3600, // optional. Time to live parameter — the maximum message // lifespan in seconds. "adm_priority": -1, // optional. Priority of the push in Amazon push drawer, // valid values are -2, -1, 0, 1 and 2. // Mac OS X related parameters "mac_badges": 3, "mac_sound": "sound.caf", "mac_sound_off": true, "mac_root_params": { "content-available": 1 }, "mac_ttl": 3600, // optional. Time to live parameter — maximum message lifespan in seconds. "mac_title": { // optional. Adds Title for push notification. "en": "title" }, "mac_subtitle": { // optional. Adds subtitle for MacOS push notification. "en": "subtitle" }, "mac_content": { // optional. Adds content for MacOS push notification. "en": "content" }, // Windows related parameters "wns_content": { // required. Content (XML or raw) of notification encoded // in MIME's base64 in form of Object OR String "en": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48YmFkZ2UgdmFsdWU9ImF2YWlsYWJsZSIvPg==", "de": "PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48YmFkZ2UgdmFsdWU9Im5ld01lc3NhZ2UiLz4=" }, "wns_type": "Badge", // 'Tile' | 'Toast' | 'Badge' | 'Raw' "wns_tag": "myTag", // optional. Used in Tile replacement policy. // An alphanumeric string of no more than 16 characters. "wns_cache": 1, // optional. (1|0) Translates into X-WNS-Cache-Policy value. "wns_ttl": 600, // optional. Expiration time for notification in seconds. // Safari related parameters "safari_title": { // optional. Object OR string. Title of the notification. "en": "title" }, "safari_content": { // optional. Object OR string. Content of the notification. "en": "content" }, "safari_action": "Click here", // optional. "safari_url_args": [ // required. but the value may be empty "firstArgument", "secondArgument" ], "safari_ttl": 3600, // optional. Time to live parameter — the maximum // lifespan of a message in seconds. // Chrome related parameters "chrome_title": { // optional. You can specify the header of the message in this parameter. "en": "title" }, "chrome_content": { // optional. You can specify the content of the message in this parameter. "en": "content" }, "chrome_icon": "icon_URL", // optional. Full path URL to the icon or extension resources file path "chrome_gcm_ttl": 3600, // optional. Time to live parameter – maximum message lifespan in seconds. "chrome_duration": 20, // optional. Changes chrome push display time. Set to 0 to display push // until user interacts with it. "chrome_image": "image_URL", // optional. URL to large image "chrome_root_params": { // optional. Set parameters specific to messages sent to Chrome. "key": "value" }, "chrome_button_text1": "text1", // optional. "chrome_button_url1": "button1_URL", // optional. Ignored if chrome_button_text1 is not set. "chrome_button_text2": "text2", // optional. "chrome_button_url2": "button2_url", // optional. Ignored if chrome_button_text2 is not set. // Firefox related parameters "firefox_title": { // optional. Object OR string. You can specify message header here. "en": "title" }, "firefox_content": { // optional. Object OR string. You can specify message content here. "en": "content" }, "firefox_icon": "icon_URL", // optional. Full path URL to the icon or path // to the file in extension resources. "firefox_root_params": { // optional. Set parameters specific to messages sent to Firefox. "key": "value" } } } ``` The basics are very simple – all filters are performed on the **sets** of entities. ### Sets Sets are defined as: **1.** Devices subscribed to the particular app (A);\ **2.** Devices that match the specified tag values (T) or app-specific tag value (AT);\ ### Syntax Let’s try with some samples according to the list above. #### Targeting app subscribers The “A” filter defines a set of devices subscribed to a particular app: `A("XXXXX-XXXXX", ["iOS", "Android", "OsX", "Windows", "Amazon", "Safari", "Chrome", "Firefox"])` where * “XXXXX-XXXXX” – Pushwoosh Application Code * [“iOS”, “Android”, …] – array of targeted platforms. If omitted, the message will be sent to all platforms available for this app. #### Filtering by tag values The “T” filter defines a set of devices that have specified tag values assigned. `T(\"Age\", IN, [17,20])` Defines the set of the devices that have the “age” tag set to one of the values: 17, 18, 19, 20. Caution For **app-specific tags**, the “AT” filter is applied. Make sure to specify a corresponding Application Code as the first value in an AT set: `AT(“XXXXX-XXXXX”, “TagName”, EQ, “VALUE”)` ### Tags types and operators The very important thing to understand is that tags are shared between the apps, and it presents a very powerful instrument for segmenting and filtering your target users without binding yourself to a particular app. The tag could be one of the three different types: **String, Integer, List**. The tag type defines what operators you can use for a particular tag. #### String tags **Applicable operators:** * **EQ** – targets devices with a specified tag value * **IN** – targets devices with any of the specified tag values * **NOTIN** – targets devices with no specified tag values * **NOTEQ** – targets devices with a tag value not equal to a specified one * **NOTSET** – targets devices with no value for a specified tag * **ANY** – targets devices with any value set for a specified tag Examples: `T (\"Age\", EQ, 30)` – filters users in the age of 30 `T (\"favorite_color\", IN, [\"red\",\"green\",\"blue\"])` – filters users who have chosen red, green, or blue as their favorite color. `T (\"Name", NOTSET, \"\")` – target devices with no value for the Name tag. You can use numeric values with the string tags, but such values will be converted to a string. #### Integer tags **Applicable operators:** * **GTE** – greater than or equal to a specified value * **LTE**– less than or equal to a specified value * **EQ** – equal to a specified value * **BETWEEN** – between the min and max specified values * **IN** – any of specified values * **NOTIN** – no specified values assigned to a device * **NOTEQ** – devices with a tag value not equal to a specified one * **NOTSET** – devices with no value for a specified tag * **ANY** – devices with any value set for a specified tag Examples: `T (\"Level\", EQ, 14)` – filters users on the 14 level only. `T (\"Level\", BETWEEN, [1,5)` – filters users on 1, 2, 3, 4, and 5 levels. `T (\"Level", GTE, 29)` – targets users who have reached at least 29 level. #### List tags **Applicable operators:** * **IN** – devices with any of the specified tag values Example: `T("Category", IN, ["breaking_news","business","politics"])` #### Date tags **Applicable operators:** * **GTE** – greater than or equal to a specified value * **LTE**– less than or equal to a specified value * **EQ** – equal to a specified value * **BETWEEN** – between the min and max specified values * **NOTEQ** – devices with a tag value not equal to a specified one * **NOTSET** – devices with no value for a specified tag * **ANY** – devices with any value set for a specified tag Examples: `AT("7777D-322A7","Last Application Open", BETWEEN, ["2022-02-28", "2022-03-02"])` `AT("7777D-322A7","Last Application Open", GTE, "90 days ago")` ### Operations * “+” – joins two sets (equals OR) * “*” – intersects two sets (equals AND) * “\” – subtracts one set from another (equals NOT) All the operations are left-associative. ”+” and ”*” have the same priority. "" has greater priority. You can use brackets to define the priorities of the calculations. Note that “\” operation is not commutative. `A("12345-12345") \ A("67890-67890")` is not the same as `A("67890-67890") \ A("12345-12345")`. Note You cannot use any of the following targeting-related parameters in the /createTargetedMessage request: * “application” * “platforms” * “devices” * “filter” * “conditions” All the other parameters listed in [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) are supported. Important There is a known issue with the `/createTargetedMessage` method: if you don’t specify any applications in “devices_filter” section, Pushwoosh doesn’t display any applications in push details. ## getPushHistory Deprecated Note Use [**/messages:list**](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) to retrieve message history and more detailed data instead. `POST` `https://api.pushwoosh.com/json/1.3/getPushHistory` Gets message history with push details. #### Request Body | Name | Type | Description | | ------------------ | ------- | ---------------------------------------------------------------------------------------------------------------------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | limitMessages | integer | Limits the number of messages in a response. Possible values from 10 to 1000. | | source | string | Push history source. Can be null or: “CP”, “API”, “GeoZone”, “RSS”, “AutoPush”, “A/B Test”. | | searchBy | string | Possible values to search by. Can be null or: “notificationID”, “notificationCode”, “applicationCode”, “campaignCode”. | | value | string | Search value set according to the “searchBy” field. | | lastNotificationID | string | Used for pagination. Last messageId from the previous /getPushHistory call. See details below. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "rows": [{ "id": 10191611434, "code": "8071-07AD1171-77238AD1", "createDate": "2020-09-14 12:26:21", "sendDate": "2020-09-14 12:26:21", "content": { "en": "Hello!" }, "url": null, "ios_title": null, "ios_subtitle": null, "ios_root_params": null, "android_header": null, "android_root_params": null, "conditions": null, "conditions_operator": "AND", "filter_code": "E3A64-A5F3C", "filter_conditions": "#In-app Purchase(≠0)", "filter_name": "Purchased something", "geozone": null, "campaignId": "", "campaignName": "", "subscription_segments": null, "open": { "C90C0-0E786": { "IOS": 0 } }, "sent": { "C90C0-0E786": { "IOS": 1 } }, "ctr": { "C90C0-0E786": 0 } }, { "id": 10191609202, "code": "41CA-83F8E0D7-7A63822B", "createDate": "2020-09-14 12:25:55", "sendDate": "2020-09-14 12:25:55", "content": { "en": "Hi!" }, "url": null, "ios_title": null, "ios_subtitle": null, "ios_root_params": null, "android_header": null, "android_root_params": null, "conditions": null, "conditions_operator": "AND", "filter_code": null, "filter_conditions": null, "filter_name": null, "geozone": null, "campaignId": "", "campaignName": "", "subscription_segments": { "2D732-BB981": "News" }, "open": { "C90C0-0E786": { "CHROME": 0, "IOS": 0 } }, "sent": { "C90C0-0E786": { "CHROME": 1, "IOS": 2 } }, "ctr": { "C90C0-0E786": 0 } }] } } ``` Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "source": null, // optional. Possible values are null, "CP", "API", "GeoZone", // "RSS", "AutoPush", "A/B Test" "searchBy": "applicationCode", // optional. Possible values are "", "notificationID", // "notificationCode", "applicationCode", "campaignCode" "value": "C8717-703F2", // optional. Search value set according to the "searchBy" field. "lastNotificationID": 0, // optional. Used for pagination. Last messageId from the // previous /getPushHistory call. See details below. "limitMessages": 1000 // optional. Possible value from 10 to 1000. } } ``` This method will return 1000 messages from the account sorted by message Id. To get the second page, specify the last message Id of previous response in the **lastNotificationId** parameter. ### Response data types ```plaintext id -- int | 0 code -- string createDate -- string (date: %Y-%m-%d %H:%M:%S) sendDate -- string (date: %Y-%m-%d %H:%M:%S) content -- array ( dict {lang: value} | list []) title -- array ( dict {lang: value} | list []) subtitle -- array ( dict {lang: value} | list []) url -- string ios_title -- string | array ( dict {lang: value} ) | null ios_subtitle -- string | array ( dict {lang: value} ) | null ios_root_params -- dict (JSON) | null android_header -- string | array ( dict {lang: value} ) | null android_root_params -- dict (JSON) | null conditions -- list (JSON) | null conditions_operator -- string | null filter_code -- string | null filter_name -- string | null filter_conditions -- string | null geozone -- string | null campaignId -- string | "" campaignName -- string | "" subscription_segments (obsolete) -- list (JSON) | null data -- dict (JSON) | null open -- dict [dict [string: int]] | "" Example: 'open': {'AAAAA-BBBBB': {'IOS': 1, 'ANDROID': 1}} sent -- dict [dict [string: int]] | "" Example: 'sent': {'AAAAA-BBBBB': {'IOS': 10, 'ANDROID': 10}} ctr -- dict [string: int] | "" Example: {'AAAAA-BBBBB': 1} errors -- dict [string: int] | "" Example: {'ANDROID': 1, 'IOS': 1} ``` ## cancelMessage `POST` `https://api.pushwoosh.com/json/1.3/cancelMessage` Cancels the sending of a scheduled message. The message remains in Message History with the `canceled` status. Follow-up sends created from it with [Resend to non-openers](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) are canceled as well. Use `/deleteMessage` instead if you want the message removed from Message History (see the caution there about already-sent messages). #### Request Body | Name | Type | Description | | --------- | ------ | ----------------------------------------------------------------------------------------------------------------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | message* | string | The [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained in `/createMessage` response. | * 200 ```json { "status_code":200, "status_message":"OK" } ``` Note The method is only allowed for messages that are in the status of pending, waiting or processing. Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "message": "xxxx-xxxxxxx-xxxxxx" // required. The message code obtained in /createMessage response } } ``` **Status codes:** | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Message successfully canceled | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 500 | 500 | Internal error | --- # /createMessage parameters **URL:** https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/ List of parameters of the /createMessage API method --- Deprecated `/createMessage` is deprecated. New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — see the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) for a field-by-field mapping of the parameters below. Here you’ll find the descriptions of the [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) API parameters. * [Required parameters](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#required-parameters) must be included to successfully send a `/createMessage` API request and broadcast a push notification at the specified time. * [Optional parameters](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#optional-parameters) allow you to customize push notification properties. Note If you are using */createMessage* to send SMS, please refer to [Parameters for sending SMS](https://docs.pushwoosh.com/developer/api-reference/sms/#createsmsmessage). Other parameters will not be passed. ## Required parameters Required parameters are obligatory to use in [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) requests. Otherwise, the request won’t be submitted. ### application Unique code of an app created in your Pushwoosh account. App code can be found at the top left corner of the Control Panel or in response to a [`/createApplication`](https://docs.pushwoosh.com/developer/api-reference/applications/#createapplication) request. The app code is a hyphen-separated set of 10 characters (both letters and digits). ![Pushwoosh application code displayed in the Control Panel at the top left corner](https://docs.pushwoosh.com/messages-api-prerequisites-1.webp) When creating an app via the API, you’ll get an app code in response to your [`/createApplication`](https://docs.pushwoosh.com/developer/api-reference/applications/#createapplication) request. To obtain a code of a previously created app via the API, call [`/getApplications`](https://docs.pushwoosh.com/developer/api-reference/applications/#getapplications). In response to the [`/getApplications`](https://docs.pushwoosh.com/developer/api-reference/applications/#getapplications) request, you’ll receive the list of all apps created in your Pushwoosh account with their names and codes. ### auth API access token from Pushwoosh Control Panel. Go to **Settings** → **API Access** and copy a token you’d like to use or generate a new one. ![API Access settings page in Pushwoosh Control Panel showing API access tokens](https://docs.pushwoosh.com/messages-api-prerequisites-2.webp) When generating an access token, specify its permissions. Check the checkboxes for those types of activities you’re going to use the API token with. You can create app-specific API tokens by checking the Applications checkboxes. ![API token generation dialog with permissions and application checkboxes](https://docs.pushwoosh.com/messages-api-prerequisites-3.webp) ### content The string or object that defines the message content. The “content” parameter submitted with a string type value will send the same message for all recipients. String ```txt "content": "Hello world!", ``` JSON objects are used for specifying content using [Dynamic Content](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/), for example, for multi-language messages. Object ```txt "content": { "en": "Hello!", "es": "¡Hola!", "de": "Hallo!" }, ``` ### notifications The JSON array of push properties. Must include at least the required `content` and `send_date` parameters. Optional parameters to use within the “notifications” array: * [campaign](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#campaign) * [capping_days](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#capping_days) * [capping_count](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#capping_count) * [conditions](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#conditions) * [data](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#data) * [devices](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#devices) * [dynamic_content](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#dynamic_content) * [filter](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#filter) * [ignore_user_timezone](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#ignore_user_timezone) * [inbox_date](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#inbox_date) * [inbox_image](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#inbox_image) * [link](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#link) * [minimize_link](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#minimize_link) * [message_type](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#message_type) * [platforms](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) * [preset](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#preset) * [rich_media](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#rich_media) * [send_rate](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#send_rate) * [timezone](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#timezone) * [template_bindings](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#template_bindings) * [transactionId](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#transactionid) * [users](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#users) ### send_date Date and time on which the message is sent. Can be any date and time formatted as YYYY-MM-DD HH:mm or ‘now’. If set to ‘now’, the message will be sent immediately after submitting the request. ## Optional parameters ### campaign The code of a Campaign. To obtain a Campaign code, go to **Statistics** → **Aggregated statistics** and select the Campaign you’re going to use. The campaign code will be visible at the end of the page URL in the format `XXXXX-XXXXX`. **Example:** **URL:** `https://app.pushwoosh.com/applications/AAAAA-AAAAA/statistics/aggregated-message?campaignCode=XXXXX-XXXXX` **Campaign code:** `XXXXX-XXXXX` To obtain a list of Campaigns with their codes, call [`/getCampaigns`](https://docs.pushwoosh.com/developer/api-reference/campaigns/#getcampaigns). In response to the `/getCampaigns` request, you’ll receive the list of all Campaigns created for a particular app in your Pushwoosh account, with their codes, names, and descriptions. ### capping_days Period to be applied for frequency capping, in days (max 30 days). See [Frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) for details. Frequency capping is not applied to messages with `message_type: transactional`. In all other cases, frequency capping is applied, including requests where `message_type` is omitted. ### capping_count The maximum number of pushes that can be sent from a specific app to a particular device within a “capping_days” period. In case the message created exceeds the “capping_count” limit for a device, it won’t be sent to that device. See [Frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) for details. ### conditions Conditions are arrays like `[tagName, operator, operand]` used for sending targeted messages based on [Tags](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/) and their values, where: * tagName — the name of a tag to apply, * [operator](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags#tag-operators) — a value comparison operator (“EQ” | “IN” | “NOTEQ” | “NOTIN” | “LTE” | “GTE” | “BETWEEN” | “NOTSET” | “ANY”), * [operand](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags#tag-values) — Tag values of any of the following types: string | integer | array | date | boolean | list #### Operator description | | | | ----------- | -------------------------------------------------------------------------------------------------------------------------------------- | | **EQ** | tag value is equal to operand. | | **IN** | tag value intersects with operand (operand must always be an array). | | **NOTEQ** | tag value is not equal to an operand. | | **NOTIN** | tag value does not intersect with operand (operand must always be an array). | | **GTE** | tag value is greater than or equal to operand. | | **LTE** | tag value is less than or equal to operand. | | **BETWEEN** | tag value is greater than or equal to min operand value but less than or equal to max operand value (operand must always be an array). | | **NOTSET** | tag is not set. Operand is not considered. | | **ANY** | tag has any value. Operand is not considered. | #### String tags **Valid operators**: EQ, IN, NOTEQ, NOTIN, NOTSET, ANY **Valid operands:** | | | | ------------- | ---------------------------------------------------------------------------- | | **EQ, NOTEQ** | operand must be a string | | **IN, NOTIN** | operand must be an array of strings like `["value 1", "value 2", "value N"]` | | **NOTSET** | tag is not set. Operand is not considered | | **ANY** | tag has any value. Operand is not considered | #### Integer tags **Valid operators**: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE, NOTSET, ANY **Valid operands:** | | | | ----------------------- | ----------------------------------------------------------------------- | | **EQ, NOTEQ, GTE, LTE** | operand must be an integer | | **IN, NOTIN** | operand must be an array of integers like `[value 1, value 2, value N]` | | **BETWEEN** | operand must be an array of integers like `[min_value, max_value]` | | **NOTSET** | tag is not set. Operand is not considered | | **ANY** | tag has any value. Operand is not considered | #### Date tags **Valid operators**: EQ, IN, NOTEQ, NOTIN, BETWEEN, GTE, LTE, NOTSET, ANY **Valid operands:** * `"YYYY-MM-DD 00:00"` (string) * unix timestamp `1234567890` (integer) * `"N days ago"` (string) for operators EQ, BETWEEN, GTE, LTE #### Boolean tags **Valid operators**: EQ, NOTSET, ANY **Valid operands:** `0, 1, true, false` #### List tags **Valid operators**: IN, NOTIN, NOTSET, ANY **Valid operands:** operand must be an array of strings like `["value 1", "value 2", "value N"]`. Important Remember that “filter” and “conditions” parameters should not be used together.\ Also, both of them **will be ignored** if the “devices” parameter is used in the same request. Country and Language tags Language tag value is a lowercase two-letter code according to [ISO-639-1](https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). Country tag value is an UPPERCASE two-letter code according to [ISO_3166-2](https://en.wikipedia.org/wiki/ISO_3166-2). For example, to send a push notification to Portuguese-speaking subscribers in Brazil, you will need to specify the following condition: `"conditions": [["Country", "EQ", "BR"],["Language", "EQ", "pt"]]` ### conditions_operator Logical operator for conditions arrays. Possible values: AND | OR. AND is default. If the operator applied is AND (when no operator is specified, or the ‘conditions_operator’ param has the ‘AND’ value), devices simultaneously complying with all the conditions will receive the push notification. If the operator is OR, devices that comply with any of the specified conditions will receive the message. ### data JSON string or JSON object used to pass any [custom data](https://docs.pushwoosh.com/developer/guides/messaging-channels/using-custom-data) in the push payload; is passed as “u” parameter in the payload (converted to JSON string). ### devices The array of [push tokens](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#push-token) or [hwids](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid) to send targeted push notifications. If set, the message will only be sent to the devices on the list. ### dynamic_content Placeholders for [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content) to be used instead of device Tag values. The example below will send the “Hello, John!” message to every user you target. If not set, the Dynamic Content values are taken from the device Tags. ```plaintext "content": "Hello, {firstname|CapitalizeFirst}!", "dynamic_content_placeholders": { "firstname": "John", "lastname": "Doe" }, ``` ### filter The name of a [Segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) exactly as it’s created in Pushwoosh Control Panel or via a [`/createFilter`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#createfilter) API request. Go to the **Audience** → **Segments** section and check the list of Segments created. ![Segments list in the Audience section of Pushwoosh Control Panel](https://docs.pushwoosh.com/messages-api-prerequisites-7.webp) To get Segments list via the API, call the [`/listFilters`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#listfilters) API method. In response to the `/listFilters` request, you’ll receive the list of all Segments created in your Pushwoosh account, with Segments’ names, conditions, and expiration dates. ### ignore_user_timezone If set to ‘true’, sends the message at the time and date specified in the “send_date” parameter according to UTC-0. If set to ‘false’, users will receive the message at the specified local time according to their device’s settings. ### inbox_date The date until which the message should be kept in users’ [Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox). If not specified, the message will be removed from Inbox on the next day after the sending date. Note To save the message to Inbox, use at least one of the ‘inbox’ parameters: “inbox_date” or “inbox_image”. Caution Message will be removed from Inbox at 00:00:01 of the date specified, so the previous date is the last day a user can see the message in their Inbox. ### inbox_image The URL of the custom image to be shown near the message in [Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox). Note To save the message to Inbox, use at least one of the ‘inbox’ parameters: “inbox_date” or “inbox_image”. ### inbox_days The lifetime of an inbox message in days, up to 30 days. After this period, the message will be removed from the inbox. Can be used instead of the **inbox_date** parameter. ### link The URL to be opened once a user opens a push notification. ### message_type Specifies the message type. Works for every platform you send to with this method, including SMS and WhatsApp, and in `createSMSMessage` and `createWhatsAppMessage`. Available values are `marketing` and `transactional`. See [Marketing vs transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) for details. This parameter is optional. If omitted, the message is treated as marketing: it is not delivered to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) members, and SMS messages are checked against [marketing consent](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/). To send a service message to everyone, set `message_type` to `transactional`. ### minimize_link Shortener to minimize the URL submitted in the “link” parameter. Please note that push notification payload size is limited, so consider creating short URLs not to exceed the limit. Available values: 0 — do not minimize, 2 — bitly. Default = 2. Google URL shortener is disabled since March 30, 2019. ### platforms The array of platform codes to send the message to specific platforms only. Available platform codes include: `1` — iOS, `3` — Android, `7` — Mac OS X, `8` — Windows, `9` — Amazon, `10` — Safari, `11` — Chrome, `12` — Firefox, `14` — Email, `17` — Huawei, `18` — SMS, and `21` — WhatsApp. ### preset The code of a [Preset](https://docs.pushwoosh.com/product/content/push-presets/) created in Pushwoosh Control Panel or via API. To obtain a preset code, go **Content** → **Presets**, expand the preset you are going to use, and copy the **Preset Code** from preset’s details. ![Presets list in Content section showing Preset Code](https://docs.pushwoosh.com/messages-api-prerequisites-8.webp) ### rich_media The code of an [In-Apps](https://docs.pushwoosh.com/product/content/in-apps/) page you’re going to attach to your message. To obtain a code, go to **Content** → **In-Apps**, open the page you are going to use, and copy the code from the URL bar of your browser. The code is a hyphen-separated set of 10 characters (both letters and digits). ![In-Apps page in Content section with Rich Media code in the browser URL bar](https://docs.pushwoosh.com/messages-api-prerequisites-9.webp) ### send_rate Throttling to restrain the push sending speed. Valid values are from 100 to 1000 pushes/second. ### timezone Timezone to be taken into account when the message is sent on a particular date and time. If set, the device’s timezone is ignored. If ignored, the message is sent in UTC. See for supported timezones. ### template_bindings Template placeholders to use in your content template. See the [Liquid Templates guide](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/) for details. ### transactionId Unique message identifier to prevent duplicating messages in case of network problems. You can assign any ID to a message created via the [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) or [`/createTargetedMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createtargetedmessage) request. Stored on the side of Pushwoosh for 5 minutes. ### use_latest_user_device If set to `true`, delivers one push per user instead of one per device: only the device with the most recent Last Application Open among the platforms in `platforms` receives it. If none of a user’s devices on those platforms has that data, the first matching device gets it instead, so the send is never dropped. Devices without a user ID are unaffected. Applies regardless of how the audience was targeted — by `users`, `filter`, or `conditions`. Works the same way as `use_latest_user_device` in [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifysegment). Defaults to `false` (send to every device). ### users The array of [userIds](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/). User ID is a unique user identifier set by a [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/), [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice), or [`/registerEmail`](https://docs.pushwoosh.com/developer/api-reference/email-api/) API request. --- # Messaging API v2 overview **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/ Unified API for sending push, email, SMS, and messenger notifications through a single endpoint. --- Messaging API v2 is a single REST/JSON endpoint for creating outbound messages across every channel Pushwoosh supports: * Push: iOS, Android, Huawei, macOS, Amazon, Windows, Safari, Chrome, Firefox, IE * Email * SMS * Telegram, Kakao, LINE, WhatsApp, Viber **Channel** is selected by the payload type (`payload` for push / SMS / messengers, `email_payload` for email). **Targeting** is selected by the request kind (`segment` for audience segments, `transactional` for explicit device or user lists). ## Base URL ```plaintext https://api.pushwoosh.com ``` If you use a dedicated region or private deployment, confirm the exact base URL with your Pushwoosh Customer Success Manager. ## Authentication Every request must include an `Authorization` header with a server-side Pushwoosh [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Token YOUR_API_TOKEN ``` Use the same token you already issue for server-to-server API calls. Do not expose this token in client applications. ## Methods * [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/): `POST /messaging/v2/notify`. Create and send a single message (segment or transactional). * [`NotifyBatch`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/): `POST /messaging/v2/notify/batch`. Send up to 500 independent `Notify` calls in one request. * [`Cancel`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/): `POST /messaging/v2/cancel`. Cancel a previously created message that has not been delivered yet. * [`Update`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/): `POST /messaging/v2/update`. Replace a still-scheduled message with a new definition. * [`Rename`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/): `POST /messaging/v2/rename`. Set or clear a still-pending message’s campaign name. ## Request and response format * Content type: `application/json`. * Field names use `snake_case`. `oneof` groups appear as nested objects with exactly one key set. * Enum values are serialized as their string names (for example, `"IOS"`, `"MESSAGE_TYPE_MARKETING"`). * Successful responses return HTTP 200 with a JSON body; errors use the standard gRPC-Gateway error envelope — `{ "code": ..., "message": ..., "details": [...] }`. ## Quick start Send a push to a segment ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "code": "active_users", "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Hello from v2!" }, "android": { "body": "Hello from v2!" } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_MARKETING" } }' ``` ## Sending email over SMTP If a service already speaks SMTP, you can submit transactional email through the [SMTP gateway](https://docs.pushwoosh.com/developer/api-reference/smtp-gateway/) instead of calling `Notify` directly. The gateway forwards each message to this API as a transactional `Notify`, so the same authentication and email payload rules apply. ## Next steps [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Notify batch ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/) [Cancel ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) [Update ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/) [Rename ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/) [Payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) [Email payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) [SMTP gateway ](https://docs.pushwoosh.com/developer/api-reference/smtp-gateway/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Cancel **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/ Cancel a message before delivery using its message_code while the send is still pending, waiting, or processing. --- `POST` `https://api.pushwoosh.com/messaging/v2/cancel` Cancels a previously created message, identified by its `message_code`. Cancellation is available only while the message is in one of these states: * **pending:** created but not yet picked up for sending. * **waiting:** scheduled for a future send time. * **processing:** currently being prepared for delivery. Important * If the message is in `processing`, cancel only stops deliveries that have not gone out yet. Anyone who already received the message may still have it. * If the message was already canceled or has finished sending, the API returns `400`. This call is not idempotent. Check the message status before you retry. To check whether a message is still in a cancelable state, see [Checking message status](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/#checking-message-status). ## Request Authenticate with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) in the `Authorization: Token ` header. | Field | Type | Required | Description | | -------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_code` | string | Yes | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) of the message to cancel, as returned by [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) in `result.message_code`. | ### Example request ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/cancel \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "message_code": "XXXX-XXXXXXXX-XXXXXXXX" }' ``` ## Response On success, returns HTTP 200 with an empty JSON body. ```json {} ``` ## Errors Errors use the standard gRPC-Gateway error envelope: `{ "code": ..., "message": ..., "details": [...] }`. | HTTP status | Condition | | ----------- | ------------------------------------------------------------------------------------------------------- | | `400` | `message_code` is missing. | | `400` | The message is not in a cancelable state (it is no longer `pending`, `waiting`, or `processing`). | | `403` | The message belongs to another account. | | `404` | No message exists for the given `message_code`. | | `500` | An internal error occurred while loading the message or publishing the cancellation. Retry the request. | **Example** Canceling a message that no longer exists returns HTTP `404`: ```json { "code": 5, "message": "message not found", "details": [] } ``` ## Checking message status Before canceling, you can verify whether a message is still in a cancelable state. Besides reading the **Status** column in the messages table in the Control Panel ([**Campaigns → One-time messages**](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/)), you can query the status programmatically with [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist): * Pass the `message_code` in the `filters.messages_codes` array (alongside the required `filters.application`). * Read the `status` field of the matching entry in `items[]`. Note `messages:list` is part of the Statistics API and uses a different auth header than this endpoint: `Authorization: Api `. ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Update ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/) [Rename ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/) [Message statistics ](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist/) [Messaging API v2 overview ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Email payload reference **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/ Complete field reference for the v2 EmailPayload — subject, body, attachments, custom from/reply-to, and list-unsubscribe. --- Reference for the `EmailPayload` message used by [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) when sending email. Note For push / SMS / messenger payloads, see the [Payload reference](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/). ## EmailPayload * `subject` (map): subject line keyed by locale, e.g. `{"en": "Hello!", "es": "¡Hola!"}`. * `body` (string): HTML body of the email. For per-locale HTML bodies, use `email_template` with an [Email Template](https://docs.pushwoosh.com/product/content/email-content/) configured in the Control Panel. * `attachments` (array of [`Attachment`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/#attachment)): file attachments. * `list_unsubscribe` (string): custom URL for the `List-Unsubscribe` header. * `from` ([`Address`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/#address)): override the default `From` configured in the app’s email settings. * `reply_to` ([`Address`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/#address)): override the default `Reply-To` configured in the app’s email settings. * `email_template` (string): code of an [Email Template](https://docs.pushwoosh.com/product/content/email-content/) to use instead of an inline `body`. ## Attachment ```json { "name": "invoice.pdf", "content": "" } ``` | Field | Type | Description | | --------- | ------ | ----------------------------------- | | `name` | string | File name as the recipient sees it. | | `content` | string | Base64-encoded file content. | ## Address ```json { "name": "Pushwoosh", "email": "support@example.com" } ``` Both fields are optional. If `name` is omitted, the recipient sees the raw email address. ## Example: Send an email to a segment ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "segment": { "application": "XXXXX-XXXXX", "platforms": ["EMAIL"], "code": "newsletter_subscribers", "email_payload": { "subject": { "en": "Our May updates", "es": "Novedades de mayo" }, "email_template": "may-digest-2026", "from": { "name": "Pushwoosh Team", "email": "news@example.com" }, "reply_to": { "email": "replies@example.com" } }, "schedule": { "at": "2026-05-01T09:00:00Z" }, "message_type": "MESSAGE_TYPE_MARKETING" } }' ``` ## Example: Transactional email with attachment ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["EMAIL"], "users": { "list": ["customer-42"] }, "email_payload": { "subject": { "en": "Your invoice" }, "body": "

Please find your invoice attached.

", "attachments": [ { "name": "invoice.pdf", "content": "JVBERi0xLjQKJe..." } ] }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL" } }' ``` --- # Migration from v1 **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/ Field-by-field migration guide from /createMessage, /createTargetedMessage, /createEmailMessage, /createSMSMessage, /createKakaoMessage to /messaging/v2/notify. --- This guide maps every legacy `/create*Message` field onto its Messaging API v2 equivalent. Use it as a reference while porting existing integrations. Note Legacy v1 methods are deprecated but fully operational. There is no hard sunset date, so you can migrate at your own pace. For new integrations, use Messaging API v2. ## High-level differences | Aspect | v1 | v2 | | -------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------- | | Endpoint per channel | separate method per channel (`/createMessage`, `/createEmailMessage`, `/createSMSMessage`, `/createKakaoMessage`, …) | single endpoint — `POST /messaging/v2/notify` | | Auth | `auth` field in the request body | `Authorization: Token ` header | | Targeting | mixed: `filter` + `conditions` + `devices` + `users` on the same request | explicit split: `NotifySegment` vs `NotifyTransactional` | | Content | flat `content` + sibling platform blocks | nested `payload.content.localized_content.{locale}.{platform}` | | Response | `MessageCode[]` | `message_code` + optional `unknown_identifiers` | ## From `/createMessage` v1 `notifications[*]` entries become individual `Notify` requests (one message each). If a v1 call has multiple entries, issue one `Notify` per entry. **Targeting decision.** If the v1 entry uses `devices` or `users` (explicit lists), map it to `transactional`. Otherwise map it to `segment`. ### Request-level fields * `application` → `segment.application` or `transactional.application` (same app-code format). * `applications_group`: not supported in v2. Use multiple per-app requests. * `auth`: moved to the `Authorization` header, no longer in the body. * `transactionId` → `transaction_id`, a request-level field alongside `segment` / `transactional`. Same deduplication behavior as v1: a repeat call with the same value within 5 minutes returns the original `message_code` instead of resending, matched on the key alone, not the payload. See [`transaction_id`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#request-structure). ### Scheduling * `send_date` (`"YYYY-MM-DD HH:mm"` or `"now"`) → `schedule.at` (RFC 3339 UTC timestamp). To reproduce v1 `"now"`, set `schedule.at` to the current time. A timestamp in the past is sent immediately when `follow_user_timezone` is unset or `false`. With `follow_user_timezone: true`, see the [caution on a past `at`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#schedule) instead. * `ignore_user_timezone` → `schedule.follow_user_timezone`. **Inverted:** `ignore_user_timezone: true` becomes `follow_user_timezone: false`. * `timezone`: not supported. v2 always uses UTC for `at`. Convert client-side. ### Targeting * `filter` (segment name) → `segment.code`. * `conditions` (`[[tag, op, value], ...]`) → `segment.expression`. Rewrite to a [seglang](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) expression. In seglang, `*` is logical AND and app-specific tags are referenced as `Tag("", "", , )`. Example: `[["Country","EQ","BR"],["Language","EQ","pt"]]` with `conditions_operator: AND` → `Tag("XXXXX-XXXXX", "Country", EQ, "br") * Tag("XXXXX-XXXXX", "Language", EQ, "pt")`. * `conditions_operator` (`AND` / `OR`): folded into `segment.expression`. * `devices` (hwids or push tokens) → `transactional.hwids.list` or `transactional.push_tokens.list`. v2 separates the two: hwids go into `hwids`, raw push tokens into `push_tokens`. * `users` → `transactional.users.list`. * `platforms` (numeric codes `[1, 3, …]`) → `segment.platforms` or `transactional.platforms` (string enums `["IOS", "ANDROID", …]`). See [Platform enum](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#platform-enum). ### Content * `content` (string) → `payload.content.localized_content.default.{platform}.body`. In v2, content is always per-locale and per-platform. Put a plain v1 string under the special `"default"` key (catch-all translation, see [Locale selection](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#locale-selection-for-a-device)). * `content` (`{locale: text}`) → `payload.content.localized_content.{locale}.{platform}.body`. Duplicate the body into each targeted platform block. * `preset` → `payload.preset`. * `data` → `payload.custom_data`. * `rich_media` → `payload.open_action.rich_media.code`. * `link` → `payload.open_action.link.url`. * `minimize_link` (`0` or `2`) → `payload.open_action.link.shortener` (`NONE` or `BITLY`). * `inbox_image` → `payload.content.localized_content.{locale}.{platform}.inbox.image_url` (each platform block has its own `inbox`). * `inbox_date` → `payload.content.localized_content.{locale}.{platform}.inbox.expiration_date`. * `inbox_days` → `payload.content.localized_content.{locale}.{platform}.inbox.expires_in`, as a duration in seconds (for example, 7 days is `"604800s"`). ### Delivery controls * `dynamic_content` / `dynamic_content_placeholders` → `dynamic_content_placeholders` on `segment` or `transactional`. * `campaign` → `campaign` on `segment` or `transactional`. * `campaign_name` → `campaign_name` on `segment` or `transactional`, unchanged. * `capping_days` → `frequency_capping.days`. * `capping_count` → `frequency_capping.count`. * `send_rate` (int) → `send_rate.value` with `send_rate.bucket: "1s"`. * `message_type` (`"marketing"` / `"transactional"`) → `message_type` (`MESSAGE_TYPE_MARKETING` / `MESSAGE_TYPE_TRANSACTIONAL`). ### Not supported in v2 * `template_bindings`: Liquid template bindings are not available in v2. Keep using v1 if you rely on them. ### Platform-specific blocks v1 accepts platform-specific parameters at the top level of each `notifications[*]` entry (`ios`, `android`, `safari`, `chrome`, …). In v2 they move inside the locale: ```plaintext // v1 "notifications": [{ "content": "Hello", "ios": { "title": "Hi", "sound": "default.caf" }, "android": { "header": "Hi", "led": "#ff0000" } }] // v2 "payload": { "content": { "localized_content": { "en": { "ios": { "title": "Hi", "body": "Hello", "sound": "default.caf" }, "android": { "title": "Hi", "body": "Hello", "led_color": "#ff0000" } } } } } ``` Field names within platform blocks differ in places. See the [Payload reference](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) for the exact v2 names. ### Example: Before and after **v1 `/createMessage`** (push to a segment): ```json { "request": { "application": "XXXXX-XXXXX", "auth": "YOUR_API_TOKEN", "notifications": [{ "send_date": "2026-05-01 12:00", "content": "Hello!", "platforms": [1, 3], "filter": "active_users", "campaign": "YYYYY-YYYYY", "capping_days": 7, "capping_count": 3, "message_type": "marketing" }] } } ``` **v2 `/messaging/v2/notify`**: ```json { "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "code": "active_users", "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Hello!" }, "android": { "body": "Hello!" } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "frequency_capping": { "days": 7, "count": 3 }, "campaign": "YYYYY-YYYYY", "message_type": "MESSAGE_TYPE_MARKETING" } } ``` ## From `/createTargetedMessage` `/createTargetedMessage` maps to `transactional` in most cases or to `segment` if you were using it purely as a cross-app `devices_filter` with no explicit identifiers. * `devices_filter` → `segment.expression` (seglang) or `segment.filter_expression` (structured). * `content` → `payload.content.localized_content.{locale}.{platform}.body`. * All other fields, same as `/createMessage` above. ## From `/createEmailMessage` Move to `Notify` with `platforms: ["EMAIL"]` and an `email_payload` block. Full reference: [Email payload reference](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/). * `subject` → `email_payload.subject` (map keyed by locale. Wrap single-locale values in `{"en": "..."}`). * `content` (HTML) → `email_payload.body`. * `email_template` → `email_payload.email_template`. * `from` / `from_name` → `email_payload.from` (`{ "name": "...", "email": "..." }`). * `reply_to` / `reply_to_name` → `email_payload.reply_to`. * `list_unsubscribe` → `email_payload.list_unsubscribe`. * `attachments` → `email_payload.attachments` (`[{ "name": "...", "content": "" }]`). * `devices` (email addresses) → `transactional.hwids.list`. For email, the hwid is the address. * `use_auto_registration: true` → `transactional.registration_type: "REGISTRATION_TYPE_FORCE"`. It registers addresses that are new to Pushwoosh and sends to them in the same request. It works with `hwids` only. Without it, new addresses are skipped. With `return_unknown_identifiers: true`, a request where none of the addresses is registered fails with [`No recipients found`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#no-recipients-found). * Targeting, schedule, campaign, etc., same as `/createMessage`. ## From `/createSMSMessage` Move to `Notify` with `platforms: ["SMS"]`. The SMS body is delivered through the app’s configured SMS provider. Put the content in `payload.content.localized_content.{locale}.{platform}.body` on any filled platform block. SMS-specific provider options (sender ID, etc.) continue to come from the app’s SMS configuration rather than the request body. * `devices` (phone numbers in international format, e.g. `+14155552671`) → `transactional.hwids.list`. * `use_auto_registration: true` → `transactional.registration_type: "REGISTRATION_TYPE_FORCE"`, which registers new phone numbers before sending. `/createSMSMessage` has no MMS equivalent. To send MMS (subject + image attachments), use `sms.subject` and `sms.file_urls` — see [MMS](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#mms). ## From `/createKakaoMessage` Move to `Notify` with `platforms: ["KAKAO"]`, using `payload.content.localized_content.{locale}.kakao`: * `template_id` → `kakao.template`. * `content` → `kakao.content`. * `variables` → `kakao.content_variables` (JSON-stringified). ## From `/createWhatsAppMessage` Move to `Notify` with `platforms: ["WHATS_APP"]`, using `payload.content.localized_content.{locale}.whatsapp`: * `content` (free-form text) → `whatsapp.content`. Delivered by Meta only inside the 24-hour customer service window. * `content_id` → `whatsapp.content_id`. Name of a pre-approved Meta template. * `language` → `whatsapp.language`. Meta-template locale (e.g. `"en_US"`). Independent of the outer `LocalizedContent` locale key. * `content_variables` (object in v1) → `whatsapp.content_variables` (JSON-stringified object). Example v1 `{"1": "John"}` becomes v2 `"{\"1\":\"John\"}"`. * `button_url_variables` (object) → `whatsapp.button_url_variables` (JSON-stringified). * `header_variables` (object) → `whatsapp.header_variables` (JSON-stringified). * `preset` → `payload.preset` (generic preset on the payload level). * Targeting: the WhatsApp phone number that in v1 went into `devices` (e.g. `"whatsapp:+1234567890"`) goes into `transactional.hwids.list`. If you registered it via [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) against a user, you can target that user with `transactional.users.list` instead. * `use_auto_registration: true` → `transactional.registration_type: "REGISTRATION_TYPE_FORCE"`. Put the number in `transactional.hwids.list` with the `whatsapp:` prefix, as in v1, and Pushwoosh registers it before sending, so you can skip the `/registerDevice` call. See [Send to new email addresses and phone numbers](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#send-to-new-email-addresses-and-phone-numbers) for the hwid format of each channel and the 24-hour window limit. ## From `/createLineMessage` Move to `Notify` with `platforms: ["LINE"]`, using `payload.content.localized_content.{locale}.line`: * `content` (plain text) → `line.content`. * `preset` (LINE preset code) → `line.template`. The v2 field stores a code that references a LINE template configured in the Pushwoosh Control Panel. * Inline `template` (v1 image, carousel, or flex message structures): not supported directly in v2. Pre-configure the rich message as a LINE preset in the Control Panel and reference it through `line.template`. * Targeting: the v1 `devices` list (LINE user IDs registered through the SDK / `/registerDevice`) becomes `transactional.users.list` (or the hwid via `transactional.hwids.list`) in v2. ## Response differences v1 `/createMessage` returns: ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": ["XXXXX-XXXXX-AAAAA"] } } ``` v2 `Notify` returns: ```json { "result": { "message_code": "XXXXX-XXXXX-AAAAA", "unknown_identifiers": [] } } ``` Non-200 responses follow the standard gRPC-Gateway error envelope (`{ "code": ..., "message": ..., "details": [...] }`) instead of the v1 `status_code` / `status_message` pair. --- # Notify **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/ Send a single message through push, email, SMS, or a channel like WhatsApp or Facebook Messenger, using /messaging/v2/notify. --- `POST` `https://api.pushwoosh.com/messaging/v2/notify` Creates and schedules a single message. ## Request structure The request body is a `NotifyRequest` with exactly one of two kinds: * [`segment`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifysegment): target an audience segment by segment code, or — without creating a segment first — a [seglang](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) expression or a structured filter expression. * [`transactional`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifytransactional): send to an explicit list of hwids, user IDs, push tokens, or test devices. Shape ```json { "segment": { ... }, // OR "transactional": { ... }, "transaction_id": "unique-uuid" } ``` | Field | Type | Description | | ---------------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `transaction_id` | string | Optional. Idempotency key for the request — works with both `segment` and `transactional`. A repeat call with the same `transaction_id` within 5 minutes returns the original `message_code` instead of sending a duplicate message. Use a UUID or another value unique per logical send. | Tip Pass an `X-Request-ID` header to correlate the created message with your request for end-to-end tracing — see [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id). Only the key is checked, not the payload Deduplication matches on `transaction_id` alone. If a retry reuses the same `transaction_id` but sends a different `segment` / `transactional` body, the original `message_code` is still returned and the new body is silently discarded — it is not compared against the first request. Never reuse a `transaction_id` across logically different messages. Tip This is the same deduplication mechanism as `transactionId` on the legacy [Messages API](https://docs.pushwoosh.com/developer/api-reference/messages-api/), sharing the same 5-minute storage window, so a retried request is deduplicated across `/createMessage` and `Notify` alike. It is unrelated to the `transaction_id` attribute on [`PW_Conversion`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/#attributes-for-the-postevent-call) revenue events, which Pushwoosh stores as-is and does not use for deduplication. ## NotifySegment Targets users who match an audience segment or filter expression. Set exactly one of `code`, `expression`, or `filter_expression`. For `expression` and `filter_expression`, no segment needs to be created beforehand — the expression is evaluated inline for this send only. Choosing between code and inline expressions Only a saved segment referenced by `code` can use [high-speed delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/) precompilation. `expression` and `filter_expression` are evaluated at send time and are never precompiled. | Field | Type | Description | | ------------------------------ | ------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `schedule` | [`Schedule`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#schedule) | When and how to send. Required. | | `application` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `platforms` | array of [`Platform`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#platform-enum) | Platforms the message targets. | | `code` | string | [Segment code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code) of a segment saved in advance. Mutually exclusive with `expression` and `filter_expression`. | | `expression` | string | [Seglang](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/) expression, evaluated for this send only — no saved segment required. Mutually exclusive with `code` and `filter_expression`. | | `filter_expression` | `FilterExpression` | The same logic as `expression`, as a structured object instead of a seglang string. Mutually exclusive with `code` and `expression`. The `FilterExpression` schema isn’t publicly documented — request it from Pushwoosh support if you need the structured form. | | `payload` | [`Payload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) | Push / SMS / Telegram / Kakao / LINE / WhatsApp / Viber / Facebook Messenger payload. Mutually exclusive with `email_payload`. | | `email_payload` | [`EmailPayload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) | Email payload. | | `campaign` | string | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to attribute this message to. | | `campaign_name` | string | Internal name for this message, shown as the row title in Message History and in its export. Unrelated to `campaign` above. It doesn’t group messages or affect delivery. Max 255 characters, trimmed. Omit it or leave it empty to keep the row titled from the payload’s own content instead. | | `frequency_capping` | [`FrequencyCapping`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#frequencycapping) | Per-user frequency limits. | | `send_rate` | [`SendRate`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#sendrate) | Throttling for the send. | | `message_type` | [`MessageType`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#messagetype-enum) | `MESSAGE_TYPE_MARKETING` (default) or `MESSAGE_TYPE_TRANSACTIONAL`. Controls control-group filtering. | | `dynamic_content_placeholders` | map | Replaces placeholders in content. | | `meta_data` | object | Free-form metadata forwarded to downstream analytics. | | `use_latest_user_device` | bool | When `true`, delivers the message to each user’s most recently active device (the one with the latest Last Application Open) instead of every device matched by the segment. Scoped to `platforms`: only devices on those platforms are considered, and if none has Last Application Open data, the first matching device is used instead of dropping the send. Defaults to `false` (send to every device). | | `geo_zone` | [`GeoZone`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#geozone) | Optional. Narrows the audience to devices whose last recorded location is inside a circle you define in this request. It is combined with `code`, `expression`, or `filter_expression` by AND: a device must match the segment and be inside the circle. Devices with no recorded location are skipped. Not available in `NotifyTransactional`. | | `control_group` | string | Code or name of a send’s own hold-out group to apply to this message. It doesn’t narrow which [control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) apply: every enabled control group of the application holds users out of marketing sends whether this field is set or not. Mutually exclusive with `holdout_percentage`. | | `holdout_percentage` | uint32 | Draws this share of the segment, `1`–`20`, into a control group scoped to this send, on top of the application’s enabled control groups, to measure a single campaign’s impact without spending down the shared ones. `0` (default) leaves it off. Mutually exclusive with `control_group`. | Important Pushwoosh names the holdout `__holdout_` (or `__holdout_` for a send with no `campaign`) and reuses that same group, at its original percentage, for every later send sharing the code. The draw freezes the first time it’s created. This group never appears in **Settings > Control groups**, and resizing, reshuffling, disabling, or deleting it returns `FailedPrecondition`, because changing it mid-measurement would invalidate the comparison. Devices drawn into the holdout are excluded from delivery the same way as by the application’s [control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-the-control-group-affects-message-delivery). They aren’t sent to, and the tracking log records the drop as `Device is in control group`. A `message_type` of `MESSAGE_TYPE_TRANSACTIONAL` skips this exclusion entirely, same as it does for the application’s control groups. Setting `holdout_percentage` together with `control_group`, or a `holdout_percentage` above `20`, returns `400`: Invalid holdout_percentage ```json { "code": 3, "message": "invalid argument", "details": [ { "@type": "type.googleapis.com/google.rpc.BadRequest", "fieldViolations": [ { "field": "holdout_percentage", "description": "holdout_percentage and control_group are mutually exclusive" } ] }, { "@type": "type.googleapis.com/google.rpc.ErrorInfo", "reason": "InvalidHoldoutPercentage", "domain": "api.pushwoosh.com", "metadata": {} } ] } ``` ### Example: Send to a segment ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "code": "active_users", "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Hello!" }, "android": { "body": "Hello!" } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_MARKETING" } }' ``` ## NotifyTransactional Sends to an explicit list of recipients. | Field | Type | Description | | -------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `schedule` | [`Schedule`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#schedule) | Required. | | `application` | string | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `platforms` | array of [`Platform`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#platform-enum) | Platforms the message targets. | | `test_devices` | bool | If `true`, send to the app’s test devices only. | | `hwids` | `{ "list": [string, ...] }` | Send to these [hwids](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid) only. | | `users` | `{ "list": [string, ...] }` | Send to these [user IDs](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/) only. | | `push_tokens` | `{ "list": [string, ...] }` | Send to these [push tokens](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#push-token) only. | | `payload` | [`Payload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) | Push / SMS / Telegram / Kakao / LINE / WhatsApp / Viber / Facebook Messenger payload. | | `email_payload` | [`EmailPayload`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) | Email payload. | | `return_unknown_identifiers` | bool | When `true`, the response’s `unknown_identifiers` lists identifiers that were not found. | | `registration_type` | [`RegistrationType`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#registrationtype-enum) | What to do with `hwids` that aren’t registered in Pushwoosh yet. Defaults to `REGISTRATION_TYPE_DEFAULT`, which skips them. Set `REGISTRATION_TYPE_FORCE` to register and send to new email addresses or phone numbers in the same request. | | `use_latest_user_device` | bool | Only applies when you target `users`. Same behavior as in `NotifySegment` above — delivers one message per user instead of one per device, scoped to `platforms`. Defaults to `false`. | | `campaign`, `campaign_name`, `frequency_capping`, `send_rate`, `message_type`, `dynamic_content_placeholders`, `meta_data` | | See `NotifySegment` above. | `test_devices`, `hwids`, `users`, and `push_tokens` are mutually exclusive. Exactly one must be set. ### Send to new email addresses and phone numbers For email, SMS, WhatsApp, LINE, Viber, and Kakao, the hwid is the destination itself, such as the email address or the phone number. To send to an hwid that isn’t registered yet, list it in `hwids` and set `registration_type` to `REGISTRATION_TYPE_FORCE`. Pushwoosh registers it and delivers the message in the same request, so you don’t need a separate `/registerDevice` or `/registerEmail` call first. `registration_type` works only with `hwids`. With `users`, an unknown user ID isn’t created, whatever `registration_type` is set to. Write each hwid in the same format [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) stores for that channel: | Channel | hwid format | Example | | -------- | -------------------------------------------------------------------------------- | ----------------------------------- | | Email | Email address | `new.customer@example.com` | | SMS | Phone number in international format | `+14155552671` | | WhatsApp | Phone number with the `whatsapp:` prefix | `whatsapp:+14155552671` | | Kakao | Phone number with the `kakao:` prefix | `kakao:+821012345678` | | Viber | Phone number with the `viber:` prefix. Pushwoosh adds the prefix if it’s missing | `viber:+14155552671` | | LINE | LINE user ID | `U4af4980629a1b2c3d4e5f6a7b8c9d0e1` | Keep the `whatsapp:` and `kakao:` prefixes. Without them, the number is read as an SMS hwid. If that number is already registered for SMS, the send finds the SMS hwid, which doesn’t match the targeted platform, and the recipient is skipped. Pushwoosh checks phone numbers only with `return_unknown_identifiers: true`. Then an SMS, WhatsApp, or Kakao hwid that isn’t a valid phone number isn’t registered and comes back in `unknown_identifiers`. Without that flag, the value is registered as is. `REGISTRATION_TYPE_FORCE` doesn’t register hwids on push platforms, Telegram, or Facebook Messenger, as these need a token from the SDK or the messenger. An unknown hwid on these platforms is dropped silently. It doesn’t come back in `unknown_identifiers`, and nothing is sent to it. For WhatsApp, registering a number doesn’t open the 24-hour customer service window. A number that hasn’t messaged you can receive only a pre-approved template set in [`whatsapp.content_id`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#whatsapp-whatsapp), not free-form `whatsapp.content`. ### Example: Send to a new email address ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["EMAIL"], "hwids": { "list": ["new.customer@example.com"] }, "registration_type": "REGISTRATION_TYPE_FORCE", "email_payload": { "subject": { "en": "Welcome aboard" }, "body": "

Thanks for signing up!

" }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL", "return_unknown_identifiers": true } }' ``` ### Example: Transactional by user IDs ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "users": { "list": ["user-123", "user-456"] }, "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Your order has shipped." } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL", "return_unknown_identifiers": true, "use_latest_user_device": true } }' ``` ## Response ```json { "result": { "message_code": "XXXXX-XXXXX-XXXXX", "unknown_identifiers": [] } } ``` | Field | Type | Description | | --------------------- | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_code` | string | Unique [message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code). Use it with [`/getMessageDetails`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#getmessagedetails) and message statistics endpoints. | | `unknown_identifiers` | array of string | Identifiers not found on the account. Populated only for a `transactional` send with `return_unknown_identifiers: true` or with `push_tokens` targeting. Identifiers registered through `REGISTRATION_TYPE_FORCE` aren’t listed. | `/getMessageDetails` can’t find a `transactional` message sent now to fewer than 10 users and fewer than 10 devices, because such a message isn’t stored. See [Messages sent now to fewer than 10 recipients](https://docs.pushwoosh.com/developer/api-reference/messages-api/#messages-sent-now-to-fewer-than-10-recipients). ### No recipients found With `return_unknown_identifiers: true` or `push_tokens` targeting, Pushwoosh looks each recipient up before creating the message. If none of them is registered, Notify returns HTTP `400` with this error, and no message is created: ```json { "code": 9, "message": "No recipients found", "details": [ ... ] } ``` If at least one recipient is found, the request succeeds and the rest are listed in `unknown_identifiers`. To send to new email addresses or phone numbers instead, set [`registration_type`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#send-to-new-email-addresses-and-phone-numbers) to `REGISTRATION_TYPE_FORCE`. For email, invalid addresses and addresses that bounced earlier are removed before this check, without being listed in `unknown_identifiers`. If no address is left after that, Notify returns `200` with a `message_code`, but no message is created and nothing is sent. ## Shared types ### Schedule ```json { "at": "2026-05-01T12:00:00Z", "follow_user_timezone": true, "past_timezones_behaviour": "PAST_TIMEZONES_BEHAVIOUR_SEND_IMMEDIATELY" } ``` | Field | Type | Description | | -------------------------- | --------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `at` | timestamp | Absolute send time (RFC 3339). Maximum 30 days in the future. With `follow_user_timezone` unset or `false`, a past `at` is replaced with the time the request was received, so the message goes out immediately to everyone. With `follow_user_timezone: true`, a past `at` is kept as is. See the caution below. | | `after` | duration | Alternative to `at`. Send after this offset from “now” (e.g. `"3600s"`). Maximum 30 days. | | `follow_user_timezone` | bool | When `true`, each device receives the message at `at` in its local timezone. | | `past_timezones_behaviour` | enum | `PAST_TIMEZONES_BEHAVIOUR_SEND_IMMEDIATELY` (default), `PAST_TIMEZONES_BEHAVIOUR_DO_NOT_SEND`, or `PAST_TIMEZONES_BEHAVIOUR_NEXT_DAY`. Only meaningful when `follow_user_timezone` is `true` or `best_time_to_send` is set. | | `best_time_to_send` | timestamp | Optional. Segment sends only. Each device gets the message at its own best hour within 24 hours of this time. Devices without a best hour get it at the date and time of `at` / `after` in their local timezone. See [Best time to send](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#best-time-to-send). | A past send time isn't instant for everyone with follow_user_timezone With `follow_user_timezone: true`, a past `at` isn’t moved to “now”. Its UTC date and time is still split into per-device local-time slots. Timezones where that local time is still ahead receive the message at that time. Timezones where it has already passed follow `past_timezones_behaviour` instead: sent now, skipped (`DO_NOT_SEND`), or pushed a day forward (`NEXT_DAY`) at that same local time. The same split applies to the [`best_time_to_send`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#best-time-to-send) fallback. #### Best time to send `best_time_to_send` is the API counterpart of the [Best time](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#best-time-paid-subscription-required) option in the Control Panel. `at` / `after` sets the fallback for devices without enough data for a best hour. Pushwoosh takes the UTC date and time of `at` / `after` and delivers the fallback at that date and time in each device’s local timezone, as with `follow_user_timezone: true`. This applies implicitly: leave `follow_user_timezone` unset, since `true` is rejected with `BestTimeToSendWithUserTimezone`. In the example below, devices without a best hour receive the message at 18:00 local time on May 1. Where that local time has already passed when the send starts, `past_timezones_behaviour` applies. ```json { "at": "2026-05-01T18:00:00Z", "best_time_to_send": "2026-05-01T00:00:00Z" } ``` Pushwoosh rejects the request with `400` unless all of these hold: * The request uses the `segment` kind. `transactional` sends don’t support `best_time_to_send`. * `follow_user_timezone` is unset or `false`. * The send time from `at` / `after` is in the future. A past `at`, `after: "0s"`, or an omitted `after` count as “send now” and are rejected. * `best_time_to_send` is no earlier than the next full hour and no more than 30 days ahead. It is rounded down to the full hour. * The send time from `at` / `after` is no later than `best_time_to_send` plus 24 hours. The error’s `reason` (in the `ErrorInfo` detail) names the failed check: | `reason` | Cause | | -------------------------------- | --------------------------------------------------------------- | | `BestTimeToSendWithUserTimezone` | `follow_user_timezone` is `true`. | | `BestTimeToSendTooEarly` | `best_time_to_send` is earlier than the next full hour. | | `BestTimeToSendTooFarInFuture` | `best_time_to_send` is more than 30 days ahead. | | `SendDateAfterBestTimeToSend` | `at` / `after` is later than `best_time_to_send` plus 24 hours. | | `BestTimeToSendNotAllowed` | `best_time_to_send` is set on a `transactional` send. | | `BestTimeToSendWithSendNow` | `at` / `after` resolves to “now” (past `at` or zero `after`). | ### GeoZone A one-off circle for [`geo_zone`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifysegment), defined in the request itself. It isn’t a saved geozone: [geo campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/) and the [Geozones API](https://docs.pushwoosh.com/developer/api-reference/geozones/) aren’t involved, and nothing is stored in the application. ```json { "lat": 40.7128, "lng": -74.006, "range": 500 } ``` | Field | Type | Description | | ------- | ------ | ----------------------------------------------------- | | `lat` | double | Latitude of the circle center, in degrees. Required. | | `lng` | double | Longitude of the circle center, in degrees. Required. | | `range` | double | Circle radius in meters. Required. | Caution The API doesn’t validate these values. An omitted field is read as `0`, and an out-of-range `lat` / `lng` or a `range` of `0` isn’t rejected. Such a send is accepted with a message code but matches few or no devices, instead of returning `400`. ### FrequencyCapping Per-user frequency limits for marketing sends. To disable capping, omit `frequency_capping` entirely, or send `days: 0` together with `count: 0`. ```json { "days": 7, "count": 3, "exclude": false, "avoid": true } ``` * `days` (int, 1–30, or `0` to disable capping): look-back window. Must be sent together with `count` — if one is `0`, the other must also be `0`; sending one as `0` while the other is nonzero returns `400`. * `count` (int, 1 or higher, or `0` to disable capping): maximum messages allowed within `days`. Same pairing rule as `days` above. * `exclude` (bool): hard-exclude users who have already hit the cap. * `avoid` (bool): soft-avoid users who have already hit the cap (they still count toward analytics). Important Sending `days` and `count` with mismatched zero-ness (e.g. `{"days": 0, "count": 5}`) returns `400`. This validation is a breaking change to `Notify` — clients that previously relied on a lone `0` being silently ignored will now get an error instead. ### SendRate ```json { "value": 500, "bucket": "1s", "avoid": false } ``` Throttles the send. `value` is messages per `bucket`; typical `bucket` is `"1s"`. ### Platform enum `IOS`, `ANDROID`, `OSX`, `WINDOWS`, `AMAZON`, `SAFARI`, `CHROME`, `FIREFOX`, `IE`, `EMAIL`, `HUAWEI_ANDROID`, `SMS`, `WEB`, `KAKAO`, `TELEGRAM`, `LINE`, `WHATS_APP`, `VIBER`, `FB_MESSENGER`. ### RegistrationType enum * `REGISTRATION_TYPE_DEFAULT`: skips identifiers that aren’t registered in Pushwoosh. Default. * `REGISTRATION_TYPE_NONE`: sends to every listed hwid without saving a device for it. * `REGISTRATION_TYPE_FORCE`: registers each unknown hwid as a device on the targeted channel, then sends to it. Applies to email, SMS, WhatsApp, LINE, Viber, and Kakao. ### MessageType enum * `MESSAGE_TYPE_UNSPECIFIED`: equivalent to `MESSAGE_TYPE_MARKETING`. * `MESSAGE_TYPE_MARKETING`: subject to control-group filtering and frequency capping. * `MESSAGE_TYPE_TRANSACTIONAL`: skips control-group filtering and frequency capping. Use for order confirmations, OTPs, and similar critical flows. ## Related [Notify batch ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/) [Cancel ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) [Payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) [Email payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Notify batch **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/ Send several messages in one call using /messaging/v2/notify/batch, one Notify per item. --- `POST` `https://api.pushwoosh.com/messaging/v2/notify/batch` Sends several messages in one call. Every item is an independent [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/): it is validated exactly like a standalone `Notify` request, and it gets its own result. Read the results, not the status code The call itself returns HTTP 200 as long as the batch envelope is valid — even if every single item failed. A failing item never fails the whole request. Always check `results[]` and the `succeeded` / `failed` counters; do not treat a 200 response as confirmation that messages were sent. This is not a faster way to send `NotifyBatch` does not send messages faster than calling `Notify` directly. If your integration can already issue requests concurrently, running `Notify` in parallel threads/workers gives the same throughput — HTTP/2 handles that by design. Use `NotifyBatch` for convenience when your integration is not built for concurrency, not for speed. ## Request structure Shape ```json { "items": [ { "item_id": "caller-defined-id", "request": { ... } } ] } ``` | Field | Type | Description | | ------- | -------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | `items` | array of `NotifyBatchItem` | Required, 1 to 500 entries (server-configured limit). Items are processed concurrently; results come back in request order regardless. | ### NotifyBatchItem | Field | Type | Description | | --------- | -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------- | | `item_id` | string | Optional caller-supplied correlation id, echoed back on the matching result. Not used by Pushwoosh otherwise. | | `request` | [`NotifyRequest`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#request-structure) | Same body as a single `Notify` call — `segment` or `transactional`, `transaction_id` included. Required. | schedule is still required per item Each item’s `request` needs its own `schedule`, exactly like a standalone `Notify` call. Omitting it does not fall back to “send now” — the item fails with an `EmptySchedule` error. Retrying a batch is safe Set `transaction_id` on each item’s `request`. A repeat call with the same `transaction_id` returns the same `message_code` for that item within the 5-minute deduplication window described on the [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#request-structure) page — so retrying the whole batch after a network error does not duplicate the messages that already went out. ### Example ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify/batch \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "items": [ { "item_id": "order-1001", "request": { "transactional": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "users": { "list": ["user-123"] }, "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Your order has shipped." } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL", "transaction_id": "order-1001-shipped" } } }, { "item_id": "order-1002", "request": { "transactional": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "users": { "list": ["user-456"] }, "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Your order has shipped." } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL", "transaction_id": "order-1002-shipped" } } } ] }' ``` ## Response ```json { "results": [ { "item_id": "order-1001", "index": 0, "result": { "message_code": "XXXXX-XXXXX-XXXXX", "unknown_identifiers": [] } }, { "item_id": "order-1002", "index": 1, "error": { "code": 3, "message": "invalid argument", "reason": "EmptySchedule", "domain": "api.pushwoosh.com" } } ], "succeeded": 1, "failed": 1 } ``` | Field | Type | Description | | ----------- | ---------------------------- | ----------------------------------------------- | | `results` | array of `NotifyBatchResult` | One entry per requested item, in request order. | | `succeeded` | integer | Count of items that returned a result. | | `failed` | integer | Count of items that returned an error. | ### NotifyBatchResult | Field | Type | Description | | --------- | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `item_id` | string | Echo of the request item’s `item_id`. | | `index` | integer | Zero-based position of the item in the request. Use this to correlate results when `item_id` was left empty. | | `result` | object | Set when the item succeeded — same shape as a single [`Notify` response](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#response): `message_code` and `unknown_identifiers`. | | `error` | `NotifyBatchError` | Set when the item failed. | ### NotifyBatchError | Field | Type | Description | | --------- | ------- | --------------------------------------------------------------------------------------------- | | `code` | integer | gRPC status code (`google.rpc.Code`), e.g. `3` for `INVALID_ARGUMENT`. | | `message` | string | Developer-facing error message, in English. | | `reason` | string | Short status name — the same value a single `Notify` call would return, e.g. `EmptySchedule`. | | `domain` | string | Error domain, `api.pushwoosh.com`. | ## Errors Errors use the standard gRPC-Gateway error envelope: `{ "code": ..., "message": ..., "details": [...] }`. These are envelope-level errors that fail the whole request — an individual item failing produces a per-item `error` in `results[]` instead (see [NotifyBatchError](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/#notifybatcherror) above), not one of these. | HTTP status | Condition | | ----------- | ------------------------------------------------------------------- | | `400` | `items` is empty. | | `400` | `items` has more than the server-configured limit (500 by default). | | `400` | One of the items is missing `request`. | **Example** Sending 501 items when the limit is 500 returns HTTP `400`: ```json { "code": 3, "message": "items must not exceed 500 entries, got 501", "details": [] } ``` ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Cancel ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) [Payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) [Messaging API v2 overview ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Payload reference **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/ Complete field reference for the v2 Payload including push content, rich media, deep links, inbox, and per-platform properties. --- Reference for the `Payload` message used by [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) when sending through any non-email channel (push, SMS, Telegram, Kakao, LINE, Viber, WhatsApp, Facebook Messenger). Note For email, see the [Email payload reference](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/). ## Payload * `preset` (string): [push preset](https://docs.pushwoosh.com/product/content/push-presets/) code (format `XXXXX-XXXXX`) to apply to this message. * `sms_preset` (string): code (format `XXXXX-XXXXX`) of a saved [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/). Its per-locale text is resolved into each locale’s [`sms.body`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#sms-sms). An inline `sms.body` for a given locale overrides the preset for that locale. The preset must belong to the same application as the message. * `content` ([`LocalizedContent`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#localizedcontent)): message content. Mutually exclusive with `silent`. * `silent` (bool): send a silent (data-only) push to every platform. Mutually exclusive with `content`. To make only some platforms silent, set `silent` inside `content` for [iOS](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#ios-ios) or [Android](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#android-android-huawei_android) instead. * `custom_data` (object): free-form JSON forwarded to the client SDK as the `u` parameter. Very large integers can lose precision, see [Large integers in custom data](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#large-integers-in-custom-data). * `custom_data_raw` (string): a string that contains a JSON object, forwarded to the client SDK as the `u` parameter. Numbers in it keep their exact value. Mutually exclusive with a non-empty `custom_data`. An invalid value returns `400`. * `open_action` ([`OpenAction`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#openaction)): action triggered when the user opens the notification. * `open_actions` (map): per-platform override of `open_action`. Key is a numeric `Platform` enum value. * `voip_push` (bool): iOS VoIP notification. ```json { "payload": { "preset": "XXXXX-XXXXX", "content": { "localized_content": { "default": { "ios": { "title": "Hello", "body": "Tap to view" } } } }, "custom_data": { "order_id": "42" }, "open_action": { "link": { "url": "https://example.com/promo" } } } } ``` ### Large integers in custom data Integers above 9007199254740992 lose precision in `custom_data`. Use `custom_data_raw` to keep them exact. `custom_data` is a `google.protobuf.Struct` (a generic JSON-like object). It stores every number as a double, a 64-bit floating-point number that is exact only for integers up to 9007199254740992. Larger integers are rounded: `5190090000030465245` reaches the device as `5190090000030465000`. Strings are not affected. To keep such integers exact, send a string that contains the JSON object in `custom_data_raw`: ```json { "payload": { "silent": true, "custom_data_raw": "{\"pushUID\": 5190090000030465245}" } } ``` Rules for `custom_data_raw`: * The string must contain a JSON object. An array, a string, `null`, or invalid JSON returns `400` (`INVALID_ARGUMENT`) with a field violation on `payload.custom_data_raw`. * If `custom_data` has any keys and `custom_data_raw` is set, the request returns `400`. An empty `custom_data` object is ignored. * Like `custom_data`, it replaces the custom data saved in the push `preset`. * The object is converted back to JSON text before delivery. Key order is not preserved and whitespace is removed. Numbers keep their exact value, but the original text is not preserved byte for byte. * In [`/messaging/v2/notify/batch`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify-batch/), an invalid `custom_data_raw` fails only its own item. Other items are sent. How you send the request: * **REST:** no changes are needed for `/messaging/v2/notify` and `/messaging/v2/notify/batch`. If `custom_data` contains an integer that a double cannot represent exactly, the API keeps it without loss automatically. You can still set `custom_data_raw` explicitly. * **gRPC:** the client builds the `Struct` itself, so precision is lost before the request is sent. Fill `custom_data_raw` instead of `custom_data`. Where the notification is delivered: * **Android:** the SDK passes `u` to the app as a string through `PushMessage.getCustomData()`, so every digit is preserved. * **Web push:** the Web SDK parses `u` with `JSON.parse`, and every JavaScript number is a double. Large integers are rounded in the browser regardless of the API, so pass them as strings. * **Windows:** the toast gets the same `u` data as other platforms, and integers sent through `custom_data_raw` arrive exact. Tip If you don’t need to calculate with a value, such as an ID, pass it as a string. Strings keep every digit at every step of delivery. ## LocalizedContent Maps locale code → per-platform content. Keys are [ISO 639-1](https://www.loc.gov/standards/iso639-2/php/code_list.php) two-letter codes (for example, `"en"`, `"es"`) plus the special key `"default"` for a catch-all translation. The exceptions to ISO 639-1 are `"zh-Hant"` and `"zh-Hans"` for Traditional and Simplified Chinese. ```json { "localized_content": { "default": { "ios": { "title": "Hello", "body": "Tap to view" }, "android": { "title": "Hello", "body": "Tap to view" } }, "es": { "ios": { "title": "Hola", "body": "Toca para ver" }, "android": { "title": "Hola", "body": "Toca para ver" } } } } ``` ### Locale selection for a device Content delivered to a device is picked in this order: 1. Exact match on the device’s language. 2. Key `"default"`. 3. Key `"en"`. 4. Any other locale present in the map. Provide at least one of `"default"` or `"en"` so every device has a deterministic fallback. If you do not expect per-locale variants, send only `"default"`. Each locale entry is a `Content` object with optional per-platform blocks. Only fill in the platforms you target. | Platform block | Channel | | ---------------- | ----------------------------------- | | `ios` | iOS push | | `android` | Android (FCM) push | | `huawei_android` | Huawei Android push | | `mac_os` | macOS push | | `amazon` | Amazon (ADM) push | | `safari` | Safari web push | | `chrome` | Chrome web push | | `firefox` | Firefox web push | | `ie` | Internet Explorer web push | | `windows` | Windows push (tile / toast / badge) | | `telegram` | Telegram message | | `kakao` | Kakao message | | `line` | LINE message | | `viber` | Viber message | | `whatsapp` | WhatsApp message | | `fb_messenger` | Facebook Messenger message | | `sms` | SMS message | ### Buttons Each locale entry can also carry `buttons`, up to 3 action buttons shown under the push on iOS, Android, and Huawei. Other platforms ignore them. Buttons need iOS SDK 7.2.10 or later with a Notification Service Extension, and Android SDK 6.17.0 or later. Each `PushButton` has these fields: * `id` (string): the button ID your app receives on a tap. Up to 64 characters, no spaces, unique within the push. Required for `ACTION_OPEN` and `ACTION_CUSTOM`. If you omit it for other actions, Pushwoosh sets `btn_1`, `btn_2`, and so on. * `label` (string): the button text, 1 to 64 characters. * `action` (enum): `ACTION_OPEN` opens the app, `ACTION_URL` opens `url`, `ACTION_DEEPLINK` opens a deep link, `ACTION_CUSTOM` passes `id` to your app, and your app handles it. * `url` (string): an absolute `http` or `https` address for `ACTION_URL`, or a deep link with a scheme for `ACTION_DEEPLINK`. * `platform_urls` (map): a deep link per platform for `ACTION_DEEPLINK`, keyed by platform code: `1` iOS, `3` Android, `17` Huawei. * `style` (enum): `STYLE_DEFAULT` or `STYLE_DESTRUCTIVE`. The destructive style shows the button in red on iOS. ```json { "localized_content": { "default": { "ios": { "title": "Your order is on its way", "body": "Arrives tomorrow" }, "android": { "title": "Your order is on its way", "body": "Arrives tomorrow" }, "buttons": [ { "id": "track", "label": "Track order", "action": "ACTION_DEEPLINK", "url": "myapp://orders/42" }, { "id": "cancel", "label": "Cancel order", "action": "ACTION_CUSTOM", "style": "STYLE_DESTRUCTIVE" } ] } } } ``` ## Common push fields These fields are shared by `ios`, `android`, `huawei_android`, `mac_os`, `amazon`, `safari`, `chrome`, and `firefox` blocks (support varies. Unused fields are ignored by the relevant platform). * `title` (string): notification title. * `body` (string): notification body. * `time_to_live` (duration, e.g. `"3600s"`): how long the push server should retain the notification for an offline device. * `sound` (string): sound file name. * `sound_enabled` (bool): enable or suppress sound. * `badges` (string): badge count (iOS) or analogue. * `root_params` (object): raw platform-specific payload overrides. * `inbox` ([`Inbox`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#inbox)): [Message Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) entry. ```json { "android": { "title": "Hello", "body": "Tap to view", "time_to_live": "3600s", "sound": "default", "sound_enabled": true, "badges": "+1" } } ``` ## iOS (`ios`) * `subtitle` (string): iOS notification subtitle. * `is_critical` (bool): critical alert (requires entitlement). * `attachment` (string): URL of a media attachment. * `thread_id` (string): thread identifier for grouped notifications. * `trim_content` (bool): trim content to fit. * `category_id` (string): `UNNotificationCategory` identifier for interactive actions. * `interruption_level` (string): `passive`, `active`, `time-sensitive`, or `critical`. * `collapse_id` (string): APNs collapse identifier. Notifications with the same `collapse_id` replace each other on the device. * `silent` (bool): send the iOS push as silent (data-only), while other platforms in the same message get a regular notification. Set inside [`content`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#payload), so it works together with it. To make every platform silent, use the top-level [`silent`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#payload) instead, which replaces `content`. Set it in any locale to apply it to every locale. ```json { "ios": { "title": "Hello", "body": "Tap to view", "subtitle": "New update", "attachment": "https://cdn.example.com/image.png", "interruption_level": "active", "thread_id": "promo" } } ``` ## Android (`android`, `huawei_android`) * `icon` (string): notification small icon. * `banner` (string): big-picture URL. * `delivery_priority` (`NORMAL` | `HIGH`): FCM delivery priority. * `vibration` (bool): vibration on receive. * `led_color` (string, hex): notification LED color. * `icon_background_color` (string, hex): icon background color. * `show_on_lockscreen` (bool): show on the lock screen. * `custom_icon` (string): URL of a custom icon. * `priority` ([`NotificationPriority`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#notificationpriority-enum)): in-tray priority. * `group_id` (string): notification group key. * `collapse_key` (string): FCM collapse key. Notifications with the same `collapse_key` replace each other while the device is offline. * `silent` (bool): send the push to this platform as silent (data-only), while other platforms in the same message get a regular notification. Set inside [`content`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#payload), so it works together with it. To make every platform silent, use the top-level [`silent`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#payload) instead, which replaces `content`. Set it in any locale to apply it to every locale. The `baidu_android` block also accepts `silent`, with the same behavior. ```json { "android": { "title": "Hello", "body": "Tap to view", "icon": "ic_notification", "banner": "https://cdn.example.com/banner.png", "led_color": "#FF0000", "priority": "PRIORITY_HIGH", "delivery_priority": "HIGH" } } ``` ## macOS (`mac_os`) Uses the common push fields plus `subtitle` and `action` (URL opened when the user clicks the notification). ```json { "mac_os": { "title": "Hello", "body": "Tap to view", "subtitle": "New update", "action": "https://example.com/promo" } } ``` ## Amazon (`amazon`) Uses the common push fields plus `custom_icon` and `priority` ([`NotificationPriority`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#notificationpriority-enum)). ```json { "amazon": { "title": "Hello", "body": "Tap to view", "custom_icon": "https://cdn.example.com/icon.png", "priority": "PRIORITY_HIGH" } } ``` ## Safari (`safari`) * `action` (string): URL opened when the user clicks the notification. * `url_arguments` (array of string): Safari URL arguments substituted into the Web Push URL template. ```json { "safari": { "title": "Hello", "body": "Tap to view", "action": "https://example.com/promo", "url_arguments": ["promo", "2026"] } } ``` ## Chrome (`chrome`) * `icon`, `image` (string): small icon and large image URLs. * `duration` (duration): auto-close timer. * `button_text1` / `button_url1`, `button_text2` / `button_url2`: up to two action buttons. ```json { "chrome": { "title": "Hello", "body": "Tap to view", "icon": "https://cdn.example.com/icon.png", "image": "https://cdn.example.com/banner.png", "duration": "20s", "button_text1": "Open", "button_url1": "https://example.com/promo" } } ``` ## Firefox (`firefox`) Uses only `title`, `body`, `icon`, `root_params`, and `inbox`. ```json { "firefox": { "title": "Hello", "body": "Tap to view", "icon": "https://cdn.example.com/icon.png" } } ``` ## Windows (`windows`) Windows uses a different shape: ```json { "windows": { "type": "TOAST", "template": { "title": "Hello", "body": "Tap to view" }, "tag": "promo", "cache": true, "time_to_live": "3600s" } } ``` * `type` is `TILE`, `TOAST`, or `BADGE`. * `template` (structured) or `raw` (`{ "content": "" }`) — exactly one. ## Telegram (`telegram`) * `body` (string): message text. * `content_variables` (string): JSON-stringified variables for the bot-side template. ```json { "telegram": { "body": "Hello from Pushwoosh", "content_variables": "{\"name\":\"John\"}" } } ``` ## Kakao (`kakao`) * `content` (string): message content. * `template` (string): approved template code. * `content_variables` (string): JSON-stringified template variable bindings. ```json { "kakao": { "content": "Hello from Pushwoosh", "template": "welcome_v1", "content_variables": "{\"name\":\"John\"}" } } ``` ## LINE (`line`) * `content` (string): plain text body. * `template` (string): code of a LINE template configured in the Pushwoosh Control Panel (used to send image, carousel, or flex messages). For rich content, pre-configure the template in the Control Panel and reference it here. At least one of `content` or `template` must be set. ```json { "line": { "content": "Hello from Pushwoosh", "template": "promo_carousel" } } ``` ## Viber (`viber`) A Viber message is a free-text body, optionally with an image and a button or with a carousel, or a pre-approved transactional template (Omni Messaging / MStat) referenced by id and language. * `body` (string): plain text message. Required when `template_id` is not set, unless `image` is set. Required with a button or a carousel. * `image` (string): public image URL. Ignored when `template_id` is set. * `button_caption` (string): button text, up to 30 characters. Set together with `button_action`. Requires `body`. Ignored when `template_id` is set. * `button_action` (string): button link: an `http://` or `https://` URL, a `viber://` deep link, or an Omni token such as `{lp-100}` (a tracking-link placeholder that Omni Messaging resolves at send time). Set together with `button_caption`. * `carousel` (object): 2–5 swipeable cards shown under `body`. Can’t be combined with `image`, `button_caption`, `button_action`, or `template_id`. See [Carousel](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#carousel). * `template_id` (string): id of a pre-approved transactional template. When set, it takes precedence over `body`. * `template_lang` (string): template locale. Required when `template_id` is set. * `template_params` (map): key/value bindings substituted into the template, e.g. `{ "name": "John", "code": "123456" }`. * `all_devices` (bool): `false` (default) delivers to the user’s primary device only; `true` delivers to all of the user’s devices. At least one of `body`, `image`, or `template_id` must be set. When `template_id` is set, `template_lang` is required. Address Viber recipients as hwids in the form `viber:` (E.164), for example `viber:+1234567890`. Plain text: ```json { "viber": { "body": "Hello from Pushwoosh" } } ``` Transactional template: ```json { "viber": { "template_id": "e3dec4a0-c063-4b0f-96d5-cf9d629a7abe", "template_lang": "en", "template_params": { "name": "John", "code": "123456", "expires_in": "5 minutes" }, "all_devices": false } } ``` ### Carousel `carousel.items` holds 2 to 5 cards. To set up a carousel in a journey instead, see [Send a carousel](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#send-a-carousel). Each card has these fields: * `title` (string): card title, 2–38 characters. Required. * `image` (string): public `http://` or `https://` image URL. Required. * `primary_button` (object): main button. Required. * `secondary_button` (object): second button. Optional. Each button has two fields, and both are required when the button is present: * `caption` (string): button text, up to 10 characters for `primary_button` and up to 12 for `secondary_button`. * `action` (string): button link: a URL with a scheme (`http://`, `https://`, `viber://`) or an Omni token such as `{lp-100}`, the same values as `button_action`. A request that breaks any of these rules is rejected with `INVALID_ARGUMENT`. The `google.rpc.BadRequest` detail names the field, for example `viber.carousel.items[0].title`. Example: ```json { "viber": { "body": "New arrivals are in. Swipe to see them.", "carousel": { "items": [ { "title": "Running shoes", "image": "https://example.com/img/shoes.jpg", "primary_button": { "caption": "Buy now", "action": "https://example.com/shoes" }, "secondary_button": { "caption": "Details", "action": "{lp-100}" } }, { "title": "Sports backpack", "image": "https://example.com/img/backpack.jpg", "primary_button": { "caption": "Buy now", "action": "https://example.com/backpack" } } ] } } } ``` ## WhatsApp (`whatsapp`) WhatsApp messages go through Meta and are subject to Meta’s messaging rules. The key split is between free-form text (only delivered inside the 24-hour customer service window opened by an inbound message from the user) and approved templates (required for outbound initiation and for any message outside the 24-hour window). * `content` (string): free-form message text. Delivered by Meta only inside the 24-hour window. * `content_id` (string): name of a pre-approved Meta template (e.g. `"hello_world"`). Required for outbound initiation or any message outside the 24-hour window. * `language` (string): template locale that must exactly match the locale approved in Meta (e.g. `"en_US"`, `"en_GB"`). Only meaningful together with `content_id`. This is independent of the outer `LocalizedContent` key. The outer key selects content for a device, and `language` selects the Meta-template locale for that content. * `content_variables` (string): JSON object mapping body placeholders, e.g. `"{\"1\":\"John\"}"`. * `button_url_variables` (string): JSON object mapping button-URL placeholders keyed by button index, e.g. `"{\"0\":\"https://...\"}"`. * `header_variables` (string): JSON object mapping header placeholders keyed by type, e.g. `"{\"image\":\"https://...\"}"`. At least one of `content` or `content_id` must be set. ```json { "whatsapp": { "content_id": "hello_world", "language": "en_US", "content_variables": "{\"1\":\"John\"}" } } ``` ## Facebook Messenger (`fb_messenger`) Facebook Messenger messages go through Meta and are subject to Meta’s messaging rules: free-form content is only delivered inside the 24-hour customer service window opened by an inbound message from the user. Outside that window, set `message_tag` to one of Meta’s approved use cases, or Meta rejects the send. * `body` (string): plain text message. Required unless you set `message`. * `message` (string): a complete Meta Send API `message` object as a JSON string, with text, attachments, templates, or quick replies. When set, Pushwoosh sends it instead of `body`. * `message_tag` (string): required outside the 24-hour window. One of `CONFIRMED_EVENT_UPDATE`, `POST_PURCHASE_UPDATE`, `ACCOUNT_UPDATE`, `HUMAN_AGENT`. ```json { "fb_messenger": { "body": "Hello from Pushwoosh", "message_tag": "ACCOUNT_UPDATE" } } ``` Caution Pushwoosh does not independently track whether the 24-hour window is open. If the window is closed and `message_tag` is not set, the send fails with Meta’s Graph API error instead of a Pushwoosh-side validation error. For this channel there’s no separate device identifier: `hwid`, `push_token`, and `user_id` all resolve to the same value, the recipient’s Meta Page-Scoped ID (PSID). Target a specific conversation with any one of them in [`NotifyTransactional`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifytransactional). ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["FB_MESSENGER"], "hwids": { "list": [""] }, "payload": { "content": { "localized_content": { "default": { "fb_messenger": { "body": "Hello from Pushwoosh" } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" } } }' ``` ## SMS (`sms`) SMS has its own platform block inside each locale’s [`Content`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#localizedcontent), alongside `ios`, `android`, and the other messaging channels. * `body` (string): SMS text for the locale. Required when the `sms` block is present. There are two ways to supply the text: * **Inline** — set `sms.body` per locale in `localized_content`. * **From a preset** — set the payload-level [`sms_preset`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#payload) to the code (format `XXXXX-XXXXX`) of a saved [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/). Its per-locale content is resolved into `sms.body` for each locale the preset defines. An inline `sms.body` for a locale overrides the preset for that locale, so you can reuse a preset and still tweak individual languages. ```json { "payload": { "sms_preset": "XXXXX-XXXXX", "content": { "localized_content": { "default": { "sms": { "body": "Your order has shipped." } }, "es": { "sms": { "body": "Tu pedido ha sido enviado." } } } } } } ``` ### MMS Adding `subject` and `file_urls` to an `sms` block turns the message into an MMS. Only [AbleMobile](https://ablemobile.com/) has an MMS endpoint — other SMS providers ignore both fields and deliver the plain-text `body` only. * `subject` (string): MMS subject. Requires at least one entry in `file_urls` — a subject without attachments is rejected. Up to 40 ASCII characters, or 13 characters if the subject contains non-ASCII characters. * `file_urls` (array of string): up to 3 attachment URLs. Each must be an absolute `https` URL ending in `.jpg` or `.gif` — `.jpeg` and `.png` are rejected by validation, even for a genuine JPEG or PNG file, because the provider can’t decode them. Each file must also be 200 KB or smaller; AbleMobile rejects the whole send if any attachment is heavier. * `message_at` (int): index into `file_urls` (0-based) that the SMS body text is shown after. `subject` and `file_urls` support [Liquid personalization](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/), same as `body`. ```json { "sms": { "body": "Your order has shipped.", "subject": "Order update", "file_urls": [ "https://cdn.example.com/shipping-label.jpg", "https://cdn.example.com/tracking-map.gif" ], "message_at": 1 } } ``` ### RCS Adding `rcs` to an `sms` block sends a Twilio content template over [RCS (Rich Communication Services)](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) instead of `body`. Twilio delivers the template as an RCS message and falls back to an SMS built from the template’s `twilio/text` type, not from `body`. A template without a `twilio/text` type reaches RCS-capable phones only. * `rcs.content_sid` (string): SID of the Twilio content template, `HX` followed by 32 hex characters. Get it from [`ListRcsTemplates`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#list-rcs-templates) or the Twilio Console. * `rcs.content_variables` (string): JSON object of template variables, serialized as a string, for example `"{\"1\": \"{FirstName|CapitalizeFirst|there}\"}"`. Values support tags and [Liquid personalization](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/). A variable you leave out takes the template’s default value. `body` is still required in an `sms` block with `rcs`. The request is rejected unless the application sends SMS through Twilio with its own **Account SID**, **Auth token**, and **Messaging Service SID** set in the [SMS platform configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/). An application on Twilio credentials provided by Pushwoosh can’t send RCS. An [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#send-over-rcs) can carry a template per locale. With `sms_preset` in the payload, the preset’s template is used for every locale where the request doesn’t bring its own `rcs.content_sid`. ```json { "sms": { "body": "Your order has shipped.", "rcs": { "content_sid": "HXb5b62575e6e4ff6129ad7c8efe1f983e", "content_variables": "{\"1\": \"{FirstName|CapitalizeFirst|there}\", \"2\": \"A-1042\"}" } } } ``` #### List RCS templates `ListRcsTemplates` returns the application’s Twilio content templates that can be sent over RCS, read with the application’s own Twilio credentials. ```plaintext GET https://rpc-api.svc-nue.pushwoosh.com/api/sms_presets:rcs_templates?application=XXXXX-XXXXX Authorization: Api YOUR_API_TOKEN ``` * `application` (string, query): application code, format `XXXXX-XXXXX`. The response holds a `templates` array. Only templates with at least one RCS content type are listed: `twilio/card`, `twilio/carousel`, `twilio/quick-reply`, `twilio/call-to-action`, or `twilio/media`. | Field | Type | Description | | :----------------- | :-------------- | :--------------------------------------------------------------------------------------- | | `content_sid` | string | Template SID, the value for `rcs.content_sid`. | | `friendly_name` | string | Template name in Twilio. | | `language` | string | Template language code. | | `types` | array of string | Twilio content types the template defines, for example `twilio/card` and `twilio/text`. | | `variables` | object | Template variables with their default values. | | `has_sms_fallback` | boolean | `true` when the template has a `twilio/text` type, the SMS a phone without RCS receives. | ```json { "templates": [ { "content_sid": "HXb5b62575e6e4ff6129ad7c8efe1f983e", "friendly_name": "order_shipped_card", "language": "en", "types": ["twilio/card", "twilio/text"], "variables": { "1": "there", "2": "your order" }, "has_sms_fallback": true } ] } ``` `ListRcsTemplates` returns these errors: | HTTP status | Cause | | :------------------------ | :-------------------------------------------------------------------------------------------------- | | `400 Bad Request` | The application has no Twilio **Account SID** and **Auth token** of its own (`FailedPrecondition`). | | `401 Unauthorized` | Twilio rejected the application’s credentials (`Unauthenticated`). | | `503 Service Unavailable` | The Twilio Content API returned another error (`Unavailable`). | ## OpenAction Defines the action performed when the user opens the message. Exactly one of: * `rich_media` ([`RichMedia`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#richmedia)): open a [Rich Media](https://docs.pushwoosh.com/product/content/in-apps/) page. * `deep_link`: open a deep link: `{ "code": "flow-code", "params": { "key": "value" } }`. * `link` ([`Link`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#link)): open a URL. ```json { "open_action": { "deep_link": { "code": "flow-code", "params": { "promo": "summer" } } } } ``` The deeplink URL and `params` values support [Liquid personalization](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/) syntax — expressions are resolved before the deep link is opened. ### RichMedia ```json { "code": "XXXXX-XXXXX" } // by Rich Media code { "url": "https://..." } // by remote URL ``` ### Link ```json { "url": "https://example.com/promo", "shortener": "BITLY" } ``` `shortener` is `NONE` (default) or `BITLY`. ## Inbox Configures how the message appears in Message Inbox. ```json { "image_url": "https://cdn.example.com/inbox.png", "expiration_date": "2026-05-15T00:00:00Z" } ``` * `image_url` (string): image shown in the inbox entry. * `expiration_date` (timestamp): when the entry is removed from the inbox. * `expires_in` (duration, e.g. `"604800s"`): how long the entry stays in the inbox, counted from the send date (the scheduled `send_date`, or the moment of the request for immediate sends). Must be positive. Ignored when `expiration_date` is set. To set a relative lifetime instead of a fixed date, send `expires_in` alone: ```json { "image_url": "https://cdn.example.com/inbox.png", "expires_in": "604800s" } ``` If your account has a maximum inbox lifetime, an `expiration_date` or `expires_in` beyond it is rejected. ## NotificationPriority enum Controls notification priority on the target device, from `PRIORITY_MIN` (lowest) to `PRIORITY_MAX` (highest). * `PRIORITY_UNSPECIFIED` * `PRIORITY_MIN` * `PRIORITY_LOW` * `PRIORITY_DEFAULT` * `PRIORITY_HIGH` * `PRIORITY_MAX` Caution Always send `priority` as one of the string values above. The API also accepts the enum’s numeric equivalents (1-5, matching the order above), but that mapping is an internal protobuf detail, not a supported contract — don’t rely on it. An unrecognized `priority` value (a misspelled string or an out-of-range number) is not rejected: the API silently drops it, and the notification is sent with no `priority` field at all instead of returning an error. ## Example: Send a push to a segment ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "code": "active_users", "payload": { "content": { "localized_content": { "en": { "ios": { "title": "Hello", "body": "Hello, world!" }, "android": { "title": "Hello", "body": "Hello, world!" } }, "es": { "ios": { "title": "¡Hola!", "body": "¡Hola, mundo!" }, "android": { "title": "¡Hola!", "body": "¡Hola, mundo!" } } } }, "open_action": { "link": { "url": "https://example.com/promo" } } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_MARKETING" } }' ``` ## Example: Transactional push by user IDs ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "users": { "list": ["customer-42"] }, "payload": { "content": { "localized_content": { "default": { "ios": { "title": "Your order", "body": "Order #42 has shipped." }, "android": { "title": "Your order", "body": "Order #42 has shipped." } } } }, "custom_data": { "order_id": "42" } }, "schedule": { "at": "2026-05-01T12:00:00Z" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL" } }' ``` --- # Rename **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/ Set or clear a pending message's campaign name using its message_code, without changing its content, audience, or schedule. --- `POST` `https://api.pushwoosh.com/messaging/v2/rename` Sets or clears the **campaign name** of a previously created message, identified by its `message_code`. Nothing else about the message changes. Content, audience, and schedule stay exactly as they were. Rename is available only while the message is **pending** — created but not yet picked up for sending. A message that has moved to `waiting`, `processing`, or any later state can no longer be renamed. In the Control Panel this state shows as **Scheduled** in the Status column, not literally “Pending.” See [Message statuses](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#message-statuses). Important * The name is trimmed of leading and trailing whitespace and cut to 255 characters. An empty (or whitespace-only) name clears the campaign name entirely, and the message falls back to its default title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/). * This call is idempotent: sending the same name again reapplies the same value. It still touches the message’s last-modified time on every call, whether or not the name actually changed, so a repeated call can move the message to the top of Message History’s default **Last modified** sort. To check whether a message is still in a renamable state, see [Checking message status](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/#checking-message-status). ## Request Authenticate with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) in the `Authorization: Token ` header. | Field | Type | Required | Description | | --------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_code` | string | Yes | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) of the message to rename, as returned by [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) in `result.message_code`. | | `campaign_name` | string | Yes | New campaign name. Trimmed and cut to 255 characters. An empty string clears the name. | ### Example request ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/rename \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "message_code": "XXXX-XXXXXXXX-XXXXXXXX", "campaign_name": "Flash_Sale_Push" }' ``` ## Response On success, returns HTTP 200 with an empty JSON body. ```json {} ``` ## Errors Errors use the standard gRPC-Gateway error envelope: `{ "code": ..., "message": ..., "details": [...] }`. | HTTP status | Condition | | ----------- | ------------------------------------------------------------------------------------------------- | | `400` | `message_code` is missing. | | `400` | The message is not in a renamable state (it is no longer `pending`). | | `403` | The message belongs to another account. | | `404` | No message exists for the given `message_code`. | | `500` | An internal error occurred while loading the message or applying the new name. Retry the request. | **Example** Renaming a message that has already started sending returns HTTP `400`: ```json { "code": 9, "message": "message status \"waiting\" is not renamable", "details": [] } ``` ## Checking message status Before renaming, you can verify whether a message is still in a renamable state. Besides reading the **Status** column in the messages table in the Control Panel ([**Campaigns → One-time messages**](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/)), where a renamable message reads **Scheduled** rather than literally “Pending,” you can query the status programmatically with [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist): * Pass the `message_code` in the `filters.messages_codes` array (alongside the required `filters.application`). * Read the `status` field of the matching entry in `items[]`. Note `messages:list` is part of the Statistics API and uses a different auth header than this endpoint: `Authorization: Api `. ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Update ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/) [Cancel ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) [Message statistics ](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist/) [Messaging API v2 overview ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Update **URL:** https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/ Replace a scheduled message with a new definition using its message_code before the send starts. --- `POST` `https://api.pushwoosh.com/messaging/v2/update` Replaces a previously created message, identified by its `message_code`, with a new definition. The replacement is a **full replace, not a patch**: the new definition is applied exactly as sent, and the `message_code` does not change. Update is available only while the message is still **pending** — scheduled for a future send and not yet picked up for processing or delivery. Important * The `request` field is a complete [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) definition. Fields you omit are **not** carried over from the original message — they are reset. Send the full message you want, not just the changed parts. * If the message is already processing, has been delivered, was canceled, or was deleted, the API returns `400`. This call is not idempotent. Check the message status before you update. To check whether a message is still in an updatable state, see [Checking message status](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/update/#checking-message-status). ## Request Authenticate with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) in the `Authorization: Token ` header. | Field | Type | Required | Description | | -------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_code` | string | Yes | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) of the message to update, as returned by [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) in `result.message_code`. | | `request` | object | Yes | The full new definition of the message. Same shape as the [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) request body — a `segment` or `transactional` object. Validated exactly like `Notify`. | ### Example request Reschedule a segment message and change its content: ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/update \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "message_code": "XXXX-XXXXXXXX-XXXXXXXX", "request": { "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "code": "active_users", "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Updated message" }, "android": { "body": "Updated message" } } } } }, "schedule": { "at": "2026-05-02T12:00:00Z" }, "message_type": "MESSAGE_TYPE_MARKETING" } } }' ``` ## Response On success, returns HTTP 200 with the result of the updated message. The `message_code` is unchanged. ```json { "result": { "message_code": "XXXX-XXXXXXXX-XXXXXXXX", "unknown_identifiers": [] } } ``` * `message_code` (string): the same code that was passed in the request. * `unknown_identifiers` (array of string): identifiers in the new definition that were not found, when applicable (see [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/)). ## Errors Errors use the standard gRPC-Gateway error envelope: `{ "code": ..., "message": ..., "details": [...] }`. | HTTP status | Condition | | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `400` | `message_code` is missing. | | `400` | The new `request` definition is missing or invalid (it is validated exactly like [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/)). | | `400` | The message is not in an updatable state (it is no longer `pending`). | | `403` | The message belongs to another account. | | `404` | No message exists for the given `message_code`. | | `500` | An internal error occurred while loading the message or applying the update. Retry the request. | **Example** Updating a message that no longer exists returns HTTP `404`: ```json { "code": 5, "message": "message not found", "details": [] } ``` ## Checking message status Before updating, you can verify whether a message is still in an updatable state. Besides reading the **Status** column in the messages table in the Control Panel ([**Campaigns → One-time messages**](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/)), you can query the status programmatically with [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist): * Pass the `message_code` in the `filters.messages_codes` array (alongside the required `filters.application`). * Read the `status` field of the matching entry in `items[]`. Note `messages:list` is part of the Statistics API and uses a different auth header than this endpoint: `Authorization: Api `. ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Cancel ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/cancel/) [Rename ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/rename/) [Message statistics ](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist/) [Messaging API v2 overview ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) [Migration from v1 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/) --- # Object tags API (beta) **URL:** https://docs.pushwoosh.com/developer/api-reference/object-tags/ Store structured user data in Object and ObjectList tags with nested objects, write and read values, and segment by fields. --- Object tags store structured data about a user in one tag. A tag of the `Object` type holds one object with named fields, such as the last `order` with its `amount` and `status`. A tag of the `ObjectList` type holds an array of such objects, such as all `orders`. A field can itself be an object or a list of objects, such as the `address` of a `profile`. You define the fields once, write and read values through the tag API, build segments by field values, and insert the values into messages with Liquid. Beta Object tags are in beta and available on request. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to enable them for your account. Nested objects and lists come with object tags and don’t need a separate request. During the beta: * The Control Panel creates and edits object tags without nested objects and lists, see [Manage object tags in the Control Panel](https://docs.pushwoosh.com/developer/api-reference/object-tags/#manage-object-tags-in-the-control-panel). Create and change nested objects and lists through the API. * In Customer Journey, the [Set tags](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#set_tags) point can write a field that has only objects above it, such as `order.amount` or `profile.address.city`. It can’t write a whole object or anything inside a list of objects. ## How object tags work An object tag has a schema and a value. The schema is the tag name and a list of fields, each with its own type. The value is stored in the fields, separately for each user. * **Field.** Each field is a tag named `.`, for example `order.amount`. You segment by fields, and you can write a field of an `Object` tag by this name. * **`Object` value.** One object per user, for example `"order": {"amount": 120, "status": "paid"}`. * **`ObjectList` value.** An array of objects with the same fields, for example `"orders": [{"amount": 120, "status": "paid"}, {"amount": 50}]`. * **Nested object.** A field of the `OBJECT` type with fields of its own, for example `profile.address` with the field `profile.address.city`. * **Nested list.** A field of the `OBJECT_LIST` type, an array of objects inside an object tag, for example `profile.visits`. See [Nested objects and lists](https://docs.pushwoosh.com/developer/api-reference/object-tags/#nested-objects-and-lists). In the API, the tag and field types are `OBJECT` and `OBJECT_LIST`. In the Control Panel, they are **Object** and **List of objects**. On this page, a list of objects means an `ObjectList` tag or a nested list. Object tags are always User-specific, which means the value belongs to the user, not to a single device, and is copied to all of the user’s devices. See [Tags API](https://docs.pushwoosh.com/developer/api-reference/tags/) for regular tags. ## Nested objects and lists A field of an object tag can hold its own fields instead of a single value. A nested object, a field of the `OBJECT` type, holds one object. A nested list, a field of the `OBJECT_LIST` type, holds a list of objects. An element of a list can contain nested objects and lists too. Each field keeps its full dotted name, such as `profile.address.city`, and you segment by these names. For example, a `profile` tag of the `OBJECT` type and an `orders` tag of the `OBJECT_LIST` type can have this structure: ```plaintext profile OBJECT ├── address OBJECT │ ├── city STRING │ └── geo OBJECT │ └── lat STRING └── visits OBJECT_LIST └── code STRING orders OBJECT_LIST ├── sku STRING ├── meta OBJECT │ └── color STRING └── lines OBJECT_LIST └── qty INTEGER ``` ### Searchable and stored-only fields What you can do with a field depends on how many lists of objects are above it: * **No list above the field.** You can segment by a field such as `profile.address.city`, write it on its own by its full name, and use it in messages. * **One list above the field.** You can segment by a field such as `profile.visits.code` or `orders.meta.color` and use it in messages. Write it only as part of the whole list. * **Two lists above the field.** A field such as `orders.lines.qty` is stored only. Use it in [messages](https://docs.pushwoosh.com/developer/api-reference/object-tags/#use-values-in-messages), but not in segments. ## Limits Pushwoosh applies these limits to object tags, nested objects and lists included: | Limit | Value | When it’s exceeded | | :----------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------- | | Nesting depth | 3 levels of objects and lists. The tag itself is the first level, so `profile.address.geo` is the deepest allowed object | The schema method returns HTTP 400, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors) | | Fields in one object | From 1 to 20, for the tag and for each nested object or nested list | The schema method returns HTTP 400, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors) | | Fields with values in one object tag | 50, nested fields included. Fields of the `OBJECT` and `OBJECT_LIST` types don’t count | The schema method returns HTTP 400, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors) | | Full field name | 64 characters, such as `profile.address.city` | The schema method returns HTTP 400, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors) | | Elements in one list of objects | 100, for the tag and for each nested list | The whole written value is rejected | | Size of one written value | 64 KB of JSON | The whole written value is rejected | A rejected value is handled the same way as a value with a wrong field type, see [Writing replaces the whole value](https://docs.pushwoosh.com/developer/api-reference/object-tags/#writing-replaces-the-whole-value). ## Schema rules Pushwoosh checks these rules when you create an object tag or add a field: * Object and field names contain only Latin letters, digits, and `_`. * The tag and each nested object or nested list have from 1 to 20 fields. The other [limits](https://docs.pushwoosh.com/developer/api-reference/object-tags/#limits) apply too. Field names are case-insensitive, so `Amount` and `amount` are the same field. * A field has one of these types: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION`, `OBJECT`, `OBJECT_LIST`. A field of the `OBJECT` or `OBJECT_LIST` type needs its own `fields`. Timezone fields aren’t supported. * A field can’t take a name that another tag already has. If a tag `order.note` exists, adding the field `note` to `order` fails with `tag order.note already exists`. * An object can’t lose its last field. Delete the object instead. * A field or an object tag that is in use can’t be deleted. A nested object or nested list counts as in use when anything inside it is used. The error lists where it is used, for example `field is in use: segment Big buyers, in-app Spring sale`. Segments, in-apps, running Customer Journey campaigns, presets, email templates, and other items that reference the tag count as usages. ## Manage object tags in the Control Panel Create an object tag without nested objects and lists in the Control Panel: 1. On the **Tags** page of your Control Panel, click **Create tag**. 2. Enter the **Tag name**. 3. In **Value type**, choose **Object** or **List of objects**. 4. Under **Fields**, enter the name of the first field. 5. Choose the type of the field. 6. To add another field, click **Add field**. 7. Click **Create**. On the tag page, add a field or click **Remove** next to a field to delete it. The Control Panel can’t create a nested object or nested list or change its fields. It shows a nested object or nested list created through the API as one field of the **Object** or **List of objects** type. Deleting that field deletes it with everything inside it. ## Manage the schema Create and change object tags with the REST methods below. Use `/json/1.3/addTag` only for regular tags, because it can’t define fields. ### Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` ### Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). A Device API token gets HTTP 403 `a device API token cannot call this method, use an account API token`. ```plaintext Authorization: Api YOUR_API_TOKEN ``` ### Endpoints These methods manage the schema of object tags and read the values of their fields: | Method | Path | Description | | :------- | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | `POST` | `/api/tags` | Create an object tag with its fields | | `GET` | `/api/tags/{name}` | Get an object tag with its fields | | `GET` | `/api/tags` | List tags. An object tag comes with all its `fields`, nested ones included, and the fields aren’t listed as separate tags | | `POST` | `/api/tags/{name}/fields` | Add a field to an object tag, a nested object, or a nested list | | `DELETE` | `/api/tags/{name}/fields/{field}` | Delete a field, a nested object, or a nested list | | `DELETE` | `/api/tags/{name}` | Delete an object tag with all its fields | | `GET` | `/api/tags:values` | List the stored values of a field, see [Stored values of a field](https://docs.pushwoosh.com/developer/api-reference/object-tags/#stored-values-of-a-field) | `POST /api/tags` returns the object tag, and both field methods return the object named in `{name}` after the change, wrapped in `{"tag": {...}}`. `DELETE /api/tags/{name}` returns `{}`. If object tags aren’t enabled for your account, `POST /api/tags` with an object type and both field methods fail with HTTP 403 `object tags are not enabled for the account`. `GET /api/tags/{name}` and `DELETE /api/tags/{name}` return HTTP 404 `tag not found`, and `GET /api/tags` doesn’t list object tags. Without object tags enabled you can’t create one, so there is nothing to write values to. ### Create an object tag `POST` `/api/tags` ##### Request body | Name | Type | Description | | :--------------- | :----- | :--------------------------------------------------------------------------------------------------------------------------------------------------------- | | name* | string | Name of the object tag. | | type* | string | `OBJECT` for one object or `OBJECT_LIST` for a list of objects. | | fields* | array | Fields of the object, from 1 to 20. | | fields[].name* | string | Field name. The field becomes the tag `.`. | | fields[].type* | string | Field type: `INTEGER`, `STRING`, `LIST`, `DATE`, `BOOLEAN`, `PRICE`, `VERSION`, `OBJECT`, or `OBJECT_LIST`. | | fields[].fields | array | Fields of a nested object or nested list, from 1 to 20, in the same format. Required for the `OBJECT` and `OBJECT_LIST` types and ignored for other types. | Example ```json { "name": "orders", "type": "OBJECT_LIST", "fields": [ { "name": "amount", "type": "INTEGER" }, { "name": "status", "type": "STRING" } ] } ``` Response ```json { "tag": { "name": "orders", "type": "OBJECT_LIST", "system": false, "user_specific": true, "has_right_ace_modify": true, "fields": [ { "name": "amount", "type": "INTEGER", "fields": [] }, { "name": "status", "type": "STRING", "fields": [] } ] } } ``` The tag is created as User-specific regardless of the `user_specific` value. `GET /api/tags/{name}` returns the same object without the `tag` wrapper. Fields are sorted by name, and a field without nested fields comes with an empty `fields` array. To create nested objects and lists, give a field the `OBJECT` or `OBJECT_LIST` type and its own `fields`. One request creates the whole structure: Example: the profile tag from Nested objects and lists ```json { "name": "profile", "type": "OBJECT", "fields": [ { "name": "address", "type": "OBJECT", "fields": [ { "name": "city", "type": "STRING" }, { "name": "geo", "type": "OBJECT", "fields": [{ "name": "lat", "type": "STRING" }] } ] }, { "name": "visits", "type": "OBJECT_LIST", "fields": [{ "name": "code", "type": "STRING" }] } ] } ``` Nested fields come back with names relative to their object, such as `city` inside `address`. `GET /api/tags/{name}` takes only the name of the tag itself. A nested name such as `profile.address` returns HTTP 404 `tag not found`. ### Add a field `POST` `/api/tags/{name}/fields` Example: POST /api/tags/orders/fields ```json { "field": { "name": "note", "type": "STRING" } } ``` To add a field to a nested object or nested list, put its full name in `{name}`, for example `POST /api/tags/profile.address/fields` or `POST /api/tags/profile.visits/fields`. The new field can be a nested object or nested list with its own `fields`: Example: POST /api/tags/profile/fields ```json { "field": { "name": "company", "type": "OBJECT", "fields": [{ "name": "title", "type": "STRING" }] } } ``` You can write the new field right away as part of the whole tag value. Adding a field fails if the object already has 20 fields, if the field breaks another [limit](https://docs.pushwoosh.com/developer/api-reference/object-tags/#limits), or if a tag with the full field name exists, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors). ### Delete a field `DELETE` `/api/tags/{name}/fields/{field}` For example, `DELETE /api/tags/orders/fields/note` deletes the field `orders.note`. To delete a field of a nested object or nested list, put its full name in `{name}`: `DELETE /api/tags/profile.address/fields/city`. Deleting a nested object or nested list deletes everything inside it: `DELETE /api/tags/profile/fields/address` deletes `profile.address.city` and `profile.address.geo` too. You can’t delete the last field of an object, or a field, a nested object, or a nested list that is in use, see [Errors](https://docs.pushwoosh.com/developer/api-reference/object-tags/#errors). ### Errors Errors come in the format `{"code": , "message": "...", "details": [...]}`. When you create an object tag, a 400 message has the form `validate: rpc error: code = InvalidArgument desc = `, where `` is the text in the table. In these messages, `` is the full name of the object, such as `profile.address`. | HTTP status | Message | Cause | | :---------- | :----------------------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------- | | 400 | `object tag and field names must contain only letters, digits and underscores` | A name has another character. | | 400 | `: an object needs 1 to 20 fields` | On create, the tag, a nested object, or a nested list has no fields or more than 20 fields. | | 400 | `an object has at most 20 fields` | The object you add a field to already has 20 fields. | | 400 | `.: objects nest at most 3 levels deep` | An `OBJECT` or `OBJECT_LIST` field would be the fourth level. | | 400 | `an object tag holds at most 50 fields with values, nested ones included` | The object tag would have more than 50 fields with values. | | 400 | `field is given twice` | Two fields have the same name, ignoring case. | | 400 | `.: object and field names together cannot be longer than 64 characters` | The full field name is too long. | | 400 | `.: field type must be INTEGER, STRING, LIST, DATE, BOOLEAN, PRICE, VERSION, OBJECT or OBJECT_LIST` | Unsupported field type. | | 400 | `an object needs at least one field; delete the object instead` | You tried to delete the last field of an object. | | 400 | `field is in use: ...`, `tag is in use: ...` | The field, something inside the nested object or nested list, or the object tag is used in segments, in-apps, Customer Journey campaigns, or other items. | | 403 | `object tags are not enabled for the account` | Object tags aren’t enabled for the account. | | 403 | `a device API token cannot call this method, use an account API token` | The request uses a Device API token. | | 404 | `tag not found` | `GET` or `DELETE /api/tags/{name}`: no such tag, the name is a field, or object tags aren’t enabled. | | 404 | `object tag not found`, `field not found` | Field methods: no such object tag, nested object, nested list, or field. `{name}` that is a field with a value also returns `object tag not found`. | | 409 | `tag already exists` | A tag with the object tag name exists. | | 409 | `tag . already exists` | A tag with the field’s full name exists. | ## Write values Write object tag values with [`/setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) and [`/bulkSetTags`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags). These methods keep their own authentication: `/setTags` takes a Device API token in the `Authorization: Token` header, and `/bulkSetTags` takes the API access token in `auth`. Send an `Object` value as a JSON object and an `ObjectList` value as an array of objects: Example: /json/1.3/setTags ```json { "request": { "application": "XXXXX-XXXXX", "userId": "user_42", "tags": { "order": { "amount": 120, "status": "paid" }, "orders": [ { "amount": 120, "status": "paid" }, { "amount": 50 } ] } } } ``` If you set the value by HWID, it is stored on the user of that device. `POST /api/devices:set_tags` on the [base URL](https://docs.pushwoosh.com/developer/api-reference/object-tags/#base-url) of the schema methods writes object tags by the same rules. Its response lists the fields of objects as flat names such as `"profile.address.city"`. A nested object takes a JSON object, and a nested list takes an array of objects: Example: nested values in /json/1.3/setTags ```json "tags": { "profile": { "address": { "city": "Berlin", "geo": { "lat": "52.5" } }, "visits": [{ "code": "v1" }] }, "orders": [ { "sku": "A-1", "meta": { "color": "red" }, "lines": [{ "qty": 2 }, { "qty": 3 }] } ] } ``` Each field takes a value in the format of its type: | Field type | Example value | | :--------- | :------------------------------------------------------------------------- | | `INTEGER` | `120` | | `STRING` | `"paid"` | | `LIST` | `["gift", "express"]` | | `DATE` | `"2026-10-02 22:11"` (UTC), `"2026-10-02"`, or a Unix timestamp in seconds | | `BOOLEAN` | `true` or `false` | | `PRICE` | `19.95` | | `VERSION` | `"1.0.0"` | ### Writing replaces the whole value Each write replaces the stored value of the object tag: * A field missing from the new value is deleted. A field sent as `null` is deleted too. * `null`, `[]`, or `""` deletes the whole value. * `{}` deletes all fields of an `Object` tag. * A key that isn’t a field of the object is skipped. * If a field value at any depth doesn’t match the field type, the whole value of the tag is rejected and the stored value stays as it was. The same happens if the value breaks a [limit](https://docs.pushwoosh.com/developer/api-reference/object-tags/#limits): a list with more than 100 elements or more than 64 KB of JSON. `/setTags` doesn’t report a rejected value in its response and returns HTTP 200. Check the result with [`/getTags`](https://docs.pushwoosh.com/developer/api-reference/object-tags/#read-values) or use `/bulkSetTags`, which reports a result for each tag. #### Hidden fields A hidden field is a field that Pushwoosh marks as hidden for its own use. You can’t create or hide a field with the schema methods on this page. `/getTags` doesn’t return hidden fields, so a value you read and write back never contains them. What happens to them on a write depends on the type: * **`Object`.** A hidden field missing from the new value is kept, at any depth. A visible nested object missing from the new value is deleted with everything inside it, hidden fields included. Deleting the whole value deletes hidden fields too. * **`ObjectList`.** Writing the list replaces all its elements, and hidden fields of the old elements are lost. An element has no identifier, so a new element can’t be matched to an old one. The same applies to a nested list. ### Write part of an Object Write a single field, a nested object, or a nested list of an `Object` tag by its full name instead of the whole tag value. `increment` and the other operations of [`/setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) work on fields: ```json "tags": { "order.status": "shipped", "order.amount": { "operation": "increment", "value": 5 } } ``` The same works for a field of a nested object that has only objects above it, such as `"profile.address.city": "Paris"`. A nested object or nested list with only objects above it can be written on its own by its full name, such as `profile.address` or `profile.visits`. The value replaces only it, and the rest of `profile` stays as it was. The 100-element and 64 KB [limits](https://docs.pushwoosh.com/developer/api-reference/object-tags/#limits) count from this value: ```json "tags": { "profile.address": { "city": "Paris" }, "profile.visits": [{ "code": "v2" }] } ``` Nothing inside a list of objects can be written on its own, because it isn’t clear which element to change. This covers a field of an `ObjectList` tag, such as `orders.sku`, and everything inside an element, such as `orders.meta` or `profile.visits.code`. `/setTags` skips such a value. Send the whole list instead. ### Object tags aren’t created automatically A value with an object or an array of objects for a tag that doesn’t exist is skipped. Tag auto-creation doesn’t create object tags, so create the schema first with [`POST /api/tags`](https://docs.pushwoosh.com/developer/api-reference/object-tags/#create-an-object-tag). ### bulkSetTags results With `detailed=true`, [`/bulkSetTags` status](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags-status) returns one of these results for an object tag, in addition to `ok`, `tag not found`, and `wrong value. expect :`: | Result | Cause | | :---------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------- | | `object tags are stored on the user only` | The object tag isn’t User-specific. | | `cannot load the fields of the object` | Pushwoosh couldn’t read the fields of the object tag. | | `wrong value. expect an object (a map) or, for a list of objects, an array of maps` | The value doesn’t fit the object tag, a field value at any depth doesn’t match its type, or the value breaks a [limit](https://docs.pushwoosh.com/developer/api-reference/object-tags/#limits). | | `a field of a list of objects cannot be set on its own, set the whole list` | The value is set on something inside a list of objects, such as `orders.sku` or `orders.meta`. | ## Read values [`/getTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#gettags) returns an `Object` value as a JSON object and an `ObjectList` value as an array of objects: Response ```json { "status_code": 200, "status_message": "OK", "response": { "result": { "order": { "amount": 120, "status": "paid" }, "orders": [ { "amount": 120, "status": "paid" }, { "amount": 50 } ] } } } ``` Nested objects and lists come back at any depth in the same shape as you [write them](https://docs.pushwoosh.com/developer/api-reference/object-tags/#write-values), for example `"profile": {"address": {"city": "Berlin"}, "visits": [{"code": "v1"}]}`. Field values are returned the same way as regular tag values in `/getTags`: a Date as a Unix timestamp in seconds, a Price as a string such as `"120.00"`, and a Boolean as `1` or `0`. ### Stored values of a field `GET /api/tags:values` on the [base URL](https://docs.pushwoosh.com/developer/api-reference/object-tags/#base-url) lists the distinct values a field holds, with the number of subscribers for each value. Pass the field’s full name in `name`, such as `profile.address.city` or `orders.sku`. ##### Query parameters | Name | Type | Description | | :--------------- | :------ | :------------------------------------------------------------------------------------ | | application* | string | Application code, `XXXXX-XXXXX`. | | name* | string | Full name of the field. | | like | string | Return only values that contain this text, in the position set by `like_op`. | | like_op | string | `STARTS_WITH` (default), `ENDS_WITH`, or `CONTAINS`. | | order_by | string | `VALUE` (default) or `COUNT`. | | order_direction | string | `ASC` (default) or `DESC`. | | limit | integer | Number of values to return. Defaults to 100. | | offset | integer | Number of values to skip. | | page | integer | Page number, starting from `0`. Used with `per_page` instead of `limit` and `offset`. | | per_page | integer | Number of values on a page. | Example ```text GET /api/tags:values?application=XXXXX-XXXXX&name=orders.sku&order_by=COUNT&order_direction=DESC ``` Response ```json { "values": [ { "value": "A-1", "count": 120 }, { "value": "B-2", "count": 45 } ], "total": 2, "page": 0, "per_page": 100 } ``` Integer and Price fields return no values, the same as regular tags of these types. An object tag, a nested object, or a nested list itself has no values of its own, so its name returns HTTP 404 `tag not found`. ## Segment by fields Set segment conditions on fields, not on the object tag itself. In the [segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/), a field is a tag with a dotted name, and it takes the operators of its own type: ```plaintext AT("XXXXX-XXXXX", "order.amount", gte, 100) ``` A field of a nested object or nested list takes its full name, such as `profile.address.city`, `profile.visits.code`, or `orders.meta.color`. A condition on the object tag itself, a nested object, or a nested list, such as `profile.address`, fails with `object tag has no value of its own, use one of its fields`. A field with two lists of objects above it, such as `orders.lines.qty`, is [stored only](https://docs.pushwoosh.com/developer/api-reference/object-tags/#searchable-and-stored-only-fields). The segment with such a condition is saved, but counting it or sending to it fails with `a field inside a list nested in a list is stored only and cannot be searched`. ### Conditions on an ObjectList A condition on a field of an `ObjectList` tag matches a user when at least one element of the list fits the condition. Conditions on fields of the same list joined with AND must match the same element: ```plaintext AT("XXXXX-XXXXX", "orders.amount", gte, 100) * AT("XXXXX-XXXXX", "orders.status", eq, "paid") ``` This segment holds users with at least one order that is both 100 or more and paid. A user with one large unpaid order and one small paid order doesn’t match. The same element is checked for the fields of an object inside it: `orders.sku` equal to `A-1` AND `orders.meta.color` equal to `red` match a user with one order that has both. Conditions joined with OR don’t have to match the same element. Conditions on two different lists are checked on each list separately. Some operators work differently on a field of an `ObjectList` tag: * **`notset`.** No element of the list has a value in this field. A user without the list matches too. * **`noteq`.** At least one element has a value in this field, and that value differs from the given one. Elements without the field don’t count. A user with orders in statuses `paid` and `new` matches `noteq "paid"`. * **`notin` on a List field.** No element contains any of the given values. * **`notin` on a field of another type.** At least one element has a value in this field that isn’t among the given values. `notset` and `notin` on a List field aren’t tied to the same element as the other conditions joined with AND. These rules apply to nested lists, such as `profile.visits`, too. ## Use values in messages Insert fields of object tags into push notifications, emails, SMS, and messenger messages such as WhatsApp with [Liquid templates](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/). Liquid works only if content templates are enabled for your account. Write a field by its full name at any depth: ```liquid Your city is {{ profile.address.city }}. ``` Take an element of a list of objects by its index, starting from `0`: ```liquid Your first order is {{ orders[0].sku }}, quantity {{ orders[0].lines[0].qty }}. ``` An index past the end of the list, or a list the user doesn’t have, renders as an empty string. Check the element first or set a fallback with `default`: ```liquid {% if orders[0] %}Your first order is {{ orders[0].sku }}.{% endif %} Your first order is {{ orders[0].sku | default: "on its way" }}. ``` Loop over a list of objects, including a list inside an element: ```liquid {% for o in orders %}{{ o.meta.color }}{% for l in o.lines %}{{ l.qty }}{% endfor %}{% endfor %} ``` Stored-only fields, such as `orders.lines.qty`, work in templates the same way as other fields. ## Export segments with object tags [`/exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment) exports the fields of object tags: * **CSV.** Each field with a value is a separate column named by its full name, such as `order.amount` or `profile.address.city`. Object tags, nested objects, and nested lists themselves get no column. A field inside a list of objects, at any depth, holds the distinct values from all elements as a JSON array, the same way as a List tag: `["A-1","B-2"]`. The array is sorted and doesn’t show which element a value came from, so it doesn’t keep the order of elements either. To get the elements whole, export in `json` or `json_each_line`. * **`json` and `json_each_line`.** Object tags come nested, in the same shape as [`/getTags`](https://docs.pushwoosh.com/developer/api-reference/object-tags/#read-values) returns them. An `Object` tag is one entry in `tags` with its fields under `object.fields`, and a nested object inside it carries its own fields under `object.fields` too, at every level. An `ObjectList` tag is one entry with its elements in `object_list.items`, and so is a nested list. Fields have short names, are sorted by name, and carry their value under a key named after the type: `integer`, `string`, `list`, `date`, `boolean`, `price`, or `version`: ```json { "name": "profile", "object": { "fields": [ { "name": "address", "object": { "fields": [{ "name": "city", "string": "Berlin" }] } }, { "name": "visits", "object_list": { "items": [{ "fields": [{ "name": "code", "string": "v1" }] }] } } ] } } ``` An `ObjectList` tag looks like this. Integer values come as strings: ```json { "name": "orders", "object_list": { "items": [ { "fields": [{ "name": "amount", "integer": "120" }, { "name": "status", "string": "paid" }] }, { "fields": [{ "name": "amount", "integer": "50" }] } ] } } ``` An element with a nested object and a nested list looks like this: ```json { "fields": [ { "name": "meta", "object": { "fields": [{ "name": "color", "string": "red" }] } }, { "name": "lines", "object_list": { "items": [{ "fields": [{ "name": "qty", "integer": "2" }] }] } } ] } ``` If `tagsList` names an object tag, all of its fields are exported, nested ones included. --- # Presets API **URL:** https://docs.pushwoosh.com/developer/api-reference/presets-api/ Create, retrieve, list, update, delete, and clone Pushwoosh push notification presets through a REST API. --- A [push preset](https://docs.pushwoosh.com/product/content/push-presets/) is a reusable push notification template — the same object you build in the Control Panel’s push editor. This API manages push presets only; SMS, WhatsApp, Kakao, LINE, and Viber presets each have their own dedicated preset service, not covered here. Use a preset’s `code` to send it through [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) (payload `preset`) or a Customer Journey [Send push point](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#messaging-points). ## Base URL ```plaintext https://rpc-api.svc-nue.pushwoosh.com ``` All endpoints are served over HTTPS. Requests and responses use `application/json` unless noted otherwise. ## Authentication Every request must include an `Authorization` header with your [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token): ```plaintext Authorization: Api YOUR_API_TOKEN ``` ## Conventions * **Field naming:** request bodies and query/path parameters accept `lowerCamelCase` (for example, `sendType`, `localizedProperties`, `searchByName`) — the server unmarshals either casing. Responses are always marshaled using the proto field names, in `snake_case` (`localized_properties`, `platform_properties`, `per_page`, and so on). The response examples and the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object) reference below use that casing. * **`code`:** every preset response carries its own code, generated on `Create`. Pass this code to `Get`, `Update`, `UpdatePartial`, `Delete`, `Clone`, and to the messaging/journey APIs above. * **Platform keys:** the `platforms` and `open_actions` maps are keyed by the numeric [device type code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type) (`1` for iOS, `3` for Android, and so on). `platform_properties` is keyed by the platform’s enum name instead (`IOS`, `ANDROID`, `HUAWEI_ANDROID`, `OSX` — the only four platforms it covers). * **Unpopulated fields:** `Get`, `Create`, and `Clone` responses include every field of the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object), even when empty or zero-valued. `List` returns a reduced field set — see [List](https://docs.pushwoosh.com/developer/api-reference/presets-api/#list) below. `Update` and `UpdatePartial` return no preset fields at all — see the [caution](https://docs.pushwoosh.com/developer/api-reference/presets-api/#update) in their sections. ### Error responses | HTTP status | Meaning | | :-------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | | `400 Bad Request` | Invalid argument — a required field is missing or malformed, or a precondition failed (for example, cloning without a `name`). | | `401 Unauthorized` | Missing or invalid `Authorization` header. | | `403 Forbidden` | The application or preset does not belong to the caller’s account. | | `404 Not Found` | The preset or application was not found. | | `500 Internal Server Error` | Unexpected server-side failure. | `Delete` on a preset still used by a running or paused journey’s Send push point also returns `400 Bad Request` (a `FailedPrecondition` on the wire) — not `409`. Remove the preset from the journey first. `Create`, `Update`, and `UpdatePartial` also return `400 Bad Request` for an unresolvable [personalization token](https://docs.pushwoosh.com/developer/api-reference/presets-api/#personalization-tokens) — see below. ## Personalization tokens `Create`, `Update`, and `UpdatePartial` validate every [personalization token](https://docs.pushwoosh.com/product/personalization/dynamic-content/) (`{name|modifier|default}`) in `localizedTitle`, `localizedSubtitle`, and `localizedContent` (for every language in the request), and in the rich per-platform content fields the sender substitutes into (title, content, banner, icon, URL, deep-link params, and so on, for each platform). Fields outside that set, such as `richmedia`, `campaignCode`, `deeplink`, or `filterCode`, keep a token exactly as written — Pushwoosh doesn’t parse personalization syntax there. A token needs a modifier Pushwoosh recognizes, because an unmodified or misspelled one can’t be formatted and would otherwise reach users as literal braces. A token with a missing or unknown modifier (`{Tag|}`, `{Tag|typo}`) fails the call with `InvalidArgument`: ```json { "code": 3, "message": "personalization token {Tag|} has no known modifier, so it would be delivered as text; expected one of [capitalizefirst capitalizeallfirst uppercase lowercase regular base64 cent dollar comma euro jpy lira M-d-y m-d-y M d y M d Y l M d H:i m-d-y H:i]" } ``` Caution On `UpdatePartial`, this check runs against the preset’s entire saved set of substituted content fields, not just the fields in the current request — a preset that already has a broken token in an untouched field fails every subsequent `UpdatePartial` call until that token is fixed. `localizedTitle`, `localizedSubtitle`, and `localizedContent` are the exception: they’re checked only when the same call includes them. ### Accepted modifiers The Control Panel’s personalization picker already offers every modifier below to `INTEGER`/`PRICE` tags (including the date formats) and to string tags, except `base64` — that one is reachable only through the API. `gitlab.corp.pushwoosh.com/channels/sdk/pkg/dynamiccontent` is the source of truth both the Control Panel and this API validate against. | Modifier | Tag type | Notes | | :----------------------------------------------------------------- | :---------------- | :--------------------------------------------------------------- | | `capitalizefirst` | string | Case-insensitive | | `capitalizeallfirst` | string | Case-insensitive | | `uppercase` | string | Case-insensitive | | `lowercase` | string | Case-insensitive | | `regular` | string or integer | Case-insensitive, no formatting applied | | `base64` | string | Case-insensitive, API only — not offered by the CP picker | | `cent` / `dollar` / `comma` / `euro` / `jpy` / `lira` | integer | Case-insensitive | | `M-d-y`, `m-d-y`, `M d y`, `M d Y`, `l`, `M d`, `H:i`, `m-d-y H:i` | integer | Date-format modifiers, matched exactly as written, case included | ## Endpoints | Method | Path | Description | | :------- | :---------------------------- | :------------------------------------ | | `POST` | `/api/presets` | Create a new push preset | | `GET` | `/api/presets` | List an application’s push presets | | `GET` | `/api/presets/{code}` | Get a single push preset | | `PUT` | `/api/presets/{code}` | Update a push preset (full overwrite) | | `PUT` | `/api/presets/{code}:partial` | Update a push preset (partial) | | `POST` | `/api/presets/{code}:clone` | Clone a push preset | | `DELETE` | `/api/presets/{code}` | Delete a push preset | ## Create Creates a new push preset in an application and returns it with its generated code. `POST` `/api/presets` ### Request body | Parameter | Type | Required | Description | | :------------ | :------ | :------- | :------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) to create the preset in. | | `name` | string | Yes | Preset name. | | `sendType` | string | No | Channel of the preset (for example `push`). | | `isV2` | boolean | No | Pins the preset’s origin flag. Omit to default to `true` (v2); set `false` only when reproducing a legacy v1 preset. | All other fields — localized content, platforms, deep link, inbox, categories, and so on — are shared with `Update` and documented once in the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object) reference below. ##### Request example ```json { "application": "XXXXX-XXXXX", "name": "20% discount", "platforms": { "1": true, "3": true }, "localizedContent": { "default": "Get your 20% discount right now", "es": "Consigue tu 20% de descuento ahora mismo" }, "localizedTitle": { "default": "Hi there" }, "openAction": { "link": { "url": "https://example.com" } }, "categories": ["promo"] } ``` ### Response Returns `{ "preset": { ... } }`, the created [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object). ## List Lists an application’s push presets — a reduced field set, not the full object — with paging, ordering, and filtering by name or category. `GET` `/api/presets` ### Query parameters | Parameter | Type | Required | Description | | :----------------- | :--------------- | :------- | :---------------------------------------------------------------------------------------------------------------------- | | `application` | string | Yes | The application code to list presets for. | | `orderBy` | string | No | `NAME` (default), `CREATED`, or `UPDATED`. | | `orderDirection` | string | No | `ASC` (default) or `DESC`. | | `page` | integer | No | Zero-based page index. | | `perPage` | integer | No | Page size. Defaults to `100` when omitted or `0`. | | `searchByName` | string | No | Case-insensitive substring match on preset name **or** code (`ILIKE %value%`). | | `searchByCategory` | array of strings | No | Repeat the parameter to filter by any of several categories, e.g. `?searchByCategory=promo&searchByCategory=lifecycle`. | | `showHidden` | boolean | No | Include presets marked `hidden`. | Note `isDefault` is a deprecated filter kept only for the legacy send-push form in the Control Panel — don’t rely on it. ### Response Each item carries only: `name`, `code`, `platforms`, `localized_content` (plain per-locale text — **not** `localized_properties`), `localized_title`, `localized_subtitle`, `banner`, `icon`, `categories`, `journey_uuid`, `custom_data`, `is_v2`, `created`, `updated`. Every other field of the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object) — `localized_properties`, `platform_properties`, `deeplink`, `richmedia`, `url`, and so on — is omitted, even if set on the preset. | Field | Type | Description | | :--------- | :--------------- | :----------------------------------------------------------------- | | `presets` | array of objects | The current page of presets, in the reduced shape described above. | | `page` | integer | The returned page index. | | `per_page` | integer | The page size used for this response. | | `total` | integer | Total number of presets matching the filters, across all pages. | ##### Response example ```json { "presets": [ { "name": "20% discount", "code": "AAAAA-BBBBB", "platforms": { "1": true, "3": true }, "categories": ["promo"] } ], "page": 0, "per_page": 100, "total": 1 } ``` ## Get Returns a single push preset by its code, with every field of the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object) populated. `GET` `/api/presets/{code}` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :----------------- | | `code` | string | The preset’s code. | ### Response Returns `{ "preset": { ... } }`, the full [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object). ## Update Overwrites an existing push preset by code with the supplied fields. `PUT` `/api/presets/{code}` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :------------------------------ | | `code` | string | The preset’s code to overwrite. | ### Request body Same fields as [Create](https://docs.pushwoosh.com/developer/api-reference/presets-api/#create) (minus `application`), plus the rest of the [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object) fields. `sendType` is accepted but ignored — a preset’s channel can’t be changed after creation. Omitting a field doesn't always mean 'leave unchanged' `Update` rebuilds the preset’s content properties from scratch on every call: `localizedProperties`, `platformProperties`, `openAction`/`openActions`, `deeplink`, `deeplinkParams`, `richmedia`, `url`, `campaignCode`, `filterCode`, `geoZones`, `sendRate`, `cappingCount`/`cappingDays`, the three `notification*Url` fields, `remotePage`, `wnsContent`, `originalUrl`, the four `*Silent` flags, `inboxImage`/`inboxIcon`/`inboxDays`/`inboxDate`, `banner`, `icon`, `customData`, and `isV2` — omit any of these and it’s cleared (or, for `isV2`, reset to `true`). `name`, `platforms`, `categories`, `localizedTitle`, `localizedSubtitle`, and `localizedContent` are the exception: each is only touched when you send it, and left unchanged when omitted. Use [UpdatePartial](https://docs.pushwoosh.com/developer/api-reference/presets-api/#updatepartial) if you want the first group to be left-unchanged-when-omitted too. Response has no preset field `Update` returns `{}` — an empty object. The response message technically defines a `preset` field, but the server never populates it. Call `Get` afterward if you need the updated object back. ### Response An empty object on success: `{}`. ## UpdatePartial Updates only the supplied fields of an existing push preset by code, leaving unset fields unchanged. `PUT` `/api/presets/{code}:partial` ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :-------------------------- | | `code` | string | The preset’s code to patch. | ### Request body Same fields as [Update](https://docs.pushwoosh.com/developer/api-reference/presets-api/#update), minus `application`. Unlike `Update`, every field here — including `localizedProperties`, `platformProperties`, `categories`, and the rest of the content-properties group listed in [Update’s caution](https://docs.pushwoosh.com/developer/api-reference/presets-api/#update) — is left unchanged when omitted, and only touched when you send it (a map/array field you send still fully replaces the existing value for that field, it just doesn’t affect anything you didn’t include). `sendType` is likewise accepted but ignored. Note `isV2` is the one exception even here: it’s rewritten on every `UpdatePartial` call, defaulting to `true` when omitted. Send `isV2: false` explicitly if the preset must stay a v1-style preset. ##### Request example ```json { "sendRate": 500, "cappingCount": 3, "cappingDays": 7 } ``` ### Response Also an empty object — see the [caution above](https://docs.pushwoosh.com/developer/api-reference/presets-api/#update). ## Clone Duplicates an existing push preset, under a new name, into the **same** application. `POST` `/api/presets/{code}:clone` Note Unlike the Email Templates API’s `Clone`, this endpoint has no `application` parameter — it always clones within the source preset’s own application. The clone is always created with `hidden: false`, regardless of the source. ### Request body | Parameter | Type | Required | Description | | :-------- | :----- | :------- | :-------------------------------------- | | `code` | string | Yes | Code of the source preset to duplicate. | | `name` | string | Yes | Name for the new preset. | ##### Request example ```json { "code": "AAAAA-BBBBB", "name": "20% discount (copy)" } ``` ### Response Returns `{ "preset": { ... } }`, the new [Preset object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#preset-object). ## Delete Permanently deletes a push preset by code. `DELETE` `/api/presets/{code}` Caution Fails if the preset is still used by a `send_push` point in a running or paused journey — remove it from the journey first. This cannot be undone. ### Path parameters | Parameter | Type | Description | | :-------- | :----- | :--------------------------- | | `code` | string | The preset’s code to delete. | ### Response An empty object on success: `{}`. ## Object reference Field names below match what `Get`, `Create`, `Update`, and `Clone` actually return — `snake_case` proto field names (see [Conventions](https://docs.pushwoosh.com/developer/api-reference/presets-api/#conventions)). The `lowerCamelCase` form used in the request examples above works the same way on input. ### Preset object #### Identity | Field | Type | Description | | :---------- | :---------------- | :---------------------------------------------------------------------------- | | `code` | string | Generated on `Create`. Identifies this preset everywhere else in the API. | | `name` | string | Preset name. | | `send_type` | string | Channel of the preset (for example `push`). | | `is_v2` | boolean | `true` for presets created or migrated to the v2 content model. | | `system` | boolean | Marks the preset as a system/internal preset. | | `hidden` | boolean | Hides the preset from `List` results (send `showHidden: true` to include it). | | `created` | string (RFC 3339) | Creation timestamp. | | `updated` | string (RFC 3339) | Last update timestamp. | #### Targeting & content | Field | Type | Description | | :------------------------------------------------------------- | :-------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `platforms` | map | Which platforms the preset targets, keyed by [device type code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type) (e.g. `"1"` for iOS). | | `localized_properties` | map | Locale → rich per-platform content. Same shape as [`LocalizedContent`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#localizedcontent) on the `Notify` payload — one entry per platform block (`ios`, `android`, and so on). This is the primary way to set platform-specific push content. | | `localized_title` / `localized_subtitle` / `localized_content` | map | Locale → plain text. A simpler alternative to `localized_properties` for title, subtitle, and body when you don’t need per-platform overrides. | | `platform_properties` | map | Legacy per-platform overrides, keyed by platform enum name (`IOS`, `ANDROID`, `HUAWEI_ANDROID`, `OSX`). See [PlatformProperties object](https://docs.pushwoosh.com/developer/api-reference/presets-api/#platformproperties-object) below. | | `open_action` | [`OpenAction`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#openaction) | Action triggered when the user opens the notification, applied to every platform. Mutually exclusive with `open_actions` — the response sets exactly one. | | `open_actions` | map | Per-platform override of `open_action`, keyed by [device type code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type). | | `deeplink` | string | [Deep Link](https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking/) code. | | `deeplink_params` | map | Parameters passed to the deep link. | | `richmedia` | string | [Rich Media](https://docs.pushwoosh.com/product/content/rich-media/) code opened by the notification. | | `url` | string | URL opened by the notification, if not using a deep link or Rich Media. | #### Inbox | Field | Type | Description | | :------------ | :---------------- | :--------------------------------------------------------------------------------------------------- | | `inbox_image` | string | Image URL shown in the [Message Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) entry. | | `inbox_icon` | string | Icon URL shown in the Message Inbox entry. | | `inbox_days` | integer | Days the entry stays in the Message Inbox. | | `inbox_date` | string (RFC 3339) | Explicit expiration date for the Message Inbox entry, as an alternative to `inbox_days`. | #### Organization & metadata | Field | Type | Description | | :-------------- | :--------------- | :---------------------------------------------------------------------------------------------------------------------- | | `categories` | array of strings | Category names the preset is tagged with. | | `campaign_code` | string | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) this preset is attributed to. | | `filter_code` | string | [Segment / Filter code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code) this preset targets by default. | | `geo_zones` | string | [Geozone](https://docs.pushwoosh.com/developer/api-reference/geozones/) targeting, if the preset is geo-triggered. | | `journey_uuid` | string | UUID of the Customer Journey that owns this preset, if it was created from a journey’s Send push point. | | `custom_data` | object | Free-form JSON forwarded to the client SDK as the `u` parameter. | | `banner` | string | Big-picture / attachment image URL. | | `icon` | string | Custom notification icon URL. | #### Delivery limits | Field | Type | Description | | :------------------------------- | :------ | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `send_rate` | integer | Throttling for sends using this preset, in messages/second — the preset-level equivalent of `Notify`’s [`SendRate`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#sendrate). | | `capping_count` / `capping_days` | integer | Per-user frequency limit for this preset — the preset-level equivalent of `Notify`’s [`FrequencyCapping`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#frequencycapping) `count` / `days`. | #### Webhooks | Field | Type | Description | | :--------------------------- | :----- | :------------------------------------------------------------------------- | | `notification_sent_url` | string | Callback URL requested when a notification using this preset is sent. | | `notification_delivered_url` | string | Callback URL requested when a notification using this preset is delivered. | | `notification_click_url` | string | Callback URL requested when a notification using this preset is clicked. | #### Legacy fields These carry over from the v1 preset model. They’re populated for Control Panel compatibility rather than for new integrations. | Field | Type | Description | | :-------------------------------------------------------- | :------ | :-------------------------------------------------------------------------------------------- | | `remote_page` | string | Legacy remote page reference. | | `wns_content` | string | Legacy Windows toast template JSON, as accepted by the v1 `createPreset`/`getPreset` methods. | | `original_url` | string | The pre-shortening value of `url`, when `url` was replaced by a shortened link. | | `ios_silent` / `android_silent` / `huawei_android_silent` | boolean | Per-platform silent (data-only) push flags. | ### PlatformProperties object Fields available in each `platform_properties` entry (`IOS`, `ANDROID`, `HUAWEI_ANDROID`, `OSX`): | Field | Type | Description | | :----------------------- | :------ | :--------------------------------------------------------------- | | `badge` | string | Badge count override. | | `sound` | string | Sound file name. | | `sound_off` | boolean | Mute the notification sound. | | `priority` | string | In-tray priority (Android/Huawei only). | | `delivery_priority` | string | `NORMAL` or `HIGH` delivery priority (Android/Huawei only). | | `ios_interruption_level` | string | `passive`, `active`, `time-sensitive`, or `critical` (iOS only). | ## Related [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/) [Customer Journey point reference ](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/point-reference/#messaging-points/) --- # Scheduled requests **URL:** https://docs.pushwoosh.com/developer/api-reference/scheduled-requests/ Use /getResults to obtain responses for scheduled requests and retrieve statistics data from asynchronous API operations. --- To obtain a response for **every scheduled request**, `/getResults` has to be sent after the main request. Most of statistics&analytics API methods need the `/getResults` to be sent in order to get the stats files or JSON: * [getAppStats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/#getappstats) * [getCampaignStats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/) * [getEventStatistics](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#geteventstatistics) * [getTagStats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#gettagstats) * [getBouncedEmails](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#bouncedemails) ## getResults `POST` `https://api.pushwoosh.com/json/1.3/getResults` Retrieves the result of a scheduled request. #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | request_id* | string | Request ID returned by a scheduled method. | Note This `request_id` is a job token for polling this result, unrelated to the message [Request ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id) used for HTTP-level tracing. Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "request_id": "REQUEST_ID" // required. Request ID returned by the scheduled method. } } ``` * 200: Response Example Response depends on the request you’re obtaining results for, so it may look different. Below is an example response for the `/getMsgStats` request: Example Response ```json { "status_code": 200, "status_message": "OK", "response": { "formatter": "hourly", "rows": [{ "datetime": "2020-05-13 08:00:00", "action": "send", "count": 6 }, { "datetime": "2020-05-13 08:00:00", "action": "inboxopen", "count": 0 }, { "datetime": "2020-05-13 08:00:00", "action": "delivery", "count": 1 }, { "datetime": "2020-05-13 08:00:00", "action": "open", "count": 0 }], "conversion": { "send": 6, "delivery": 1, "open": 0, "events": [] } } } ``` --- # Segmentation (Filters) API **URL:** https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/ Create, retrieve, list, and delete filters, and export segments using Segmentation (Filters) API methods. --- The Segmentation (Filters) API manages filters, the sets of conditions that define a segment of your users. Use it to create, list, and delete filters, and to export the devices that match a filter for analysis or sync with an external system. You can write filter conditions in the [segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/). ## createFilter `POST` `https://api.pushwoosh.com/json/1.3/createFilter` Creates a new filter. **Request body** | Name | Required | Type | Description | | -------------------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth* | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | name* | Yes | string | [Filter name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-name). | | filter_expression* | Yes | string | Expression constructed according to the rules of the [Segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/). **Example:** `T(“City”, eq, “Madrid”)` to segment users whose city is Madrid. | | application | No | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) that scopes the filter to an app. Omit it unless your account uses high-speed delivery or requires app-scoped filters. See the note below the table. | | expiration_date | No | string | Filter expiry. The filter will be automatically deleted on a date specified, unless it’s used in a Preset or an RSS Feed. | What the application parameter creates * **Your account has [high-speed delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/) configured:** a high-speed filter. * **No high-speed delivery, but your account requires app-scoped filters:** a regular app-scoped filter. * **Neither:** the request fails with a 403 error saying `the application parameter creates a High-Speed Delivery filter, and High-Speed Delivery is not enabled for this account`. **200** ```json { "status_code": 200, "status_message": "OK", "response": { "name": "filter name" } } ``` **Example** ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", "name": "City = Madrid", "filter_expression": "T(\"City\", eq, \"Madrid\")", "application": "B18XX-XXXXX", "expiration_date": "2025-01-01" } } // creating Filters for Timezones { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // API access token from Pushwoosh Control Panel "name": "Timezone Filter", "filter_expression": "T(\"Timezone\", BETWEEN, [\"UTC-12:00\", \"UTC+14:00\"])" } } ``` ## listFilters `POST` `https://api.pushwoosh.com/json/1.3/listFilters` Returns a list of available segments (filters) with their conditions. **Request Body** | Name | Required | Type | Description | | ------------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | **200** ```json { "status_code": 200, "status_message": "OK", "response": { "filters": [{ "code": "52551-F2F42", "name": "City = Madrid", "filter_expression": "T(\"City\", eq, \"madrid\")", "expiration_date": "2025-01-01", "application": "B18XX-XXXXX" }] } } ``` **Example** ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", "application": "B18XX-XXXXX" } } ``` ## deleteFilter `POST` `https://api.pushwoosh.com/json/1.3/deleteFilter` Deletes an existing filter. **Request Body** | Name | Type | Description | | ------ | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | name* | string | [Filter name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-name). | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // API access token from Pushwoosh Control Panel "name": "filter name" } } ``` ## exportSegment `POST` `https://api.pushwoosh.com/api/v2/audience/exportSegment` A scheduled request. Exports the list of subscribers that fall under specified Filter conditions. **Request body** | Name | Required | Type | Description | | -------------------- | ------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth* | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | filterExpression* | Yes | string | Filter conditions | | exportData | No | array | Data to export. Possible values: `"hwids"`, `"push_tokens"`, `"users"`, `"tags"`, `"location"`, `"ad_identifiers"`. Including `"location"` adds `Latitude` and `Longitude` columns to the exported CSV. If `exportData` is omitted, `Latitude` and `Longitude` are included in the export by default. `"ad_identifiers"` adds `MADID`, `Email SHA256`, and `Phone SHA256` columns for building a Google Customer Match or Meta Custom Audience source file — see the [ad identifiers export](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#ad-identifiers-export) note below. | | filterCode | No | string | Pre-made [filter code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code), can be used instead of `filterExpression`. Can be obtained from `/listFilters` API or address bar of your browser when viewing the filter in Control Panel. | | applicationCode | Required if you’re using either `filterExpression` or `filterCode`. | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | generateExport | No | boolean | By default set to `true`, and a response contains a link to download the file. If false, only devices count will be sent in response. | | format | No | string | Sets the format of the exported file: “csv” or “json_each_line”. If omitted, the CSV file is generated. | | tagsList | No | array | Specifies [tags](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag) to export. To obtain the specific tags only, the “exportData” array should contain the “tags” value. | | includeWithoutTokens | No | boolean | Set to `true` to include users without push tokens in the exported file. Default is `false`. | * 200 Successful ```json { "task_id": "177458" } ``` Example ```json { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "filterExpression": "AT(\"12345-67890\", \"Name\", any)", // filter conditions, refer to the Segmentation Language guide for syntax "filterCode": "12345-67890", // pre-made filter code, can be used instead of filterExpression "applicationCode": "00000-AAAAA", // Required if you're using either `filterExpression` or `filterCode`. Pushwoosh app code. Can be obtained from /listFilters API request or address bar of your browser while viewing the filter in Control Panel. "generateExport": true, // if false, devices count only will be sent in response; by default, a response contains a link to download the CSV file "format": "json_each_line", // format of the file to present the data in: "csv" – the .csv file is downloaded; "json" – a JSON file with all expored devices; or "json_each_line" – JSON line for each device. If not specified, CSV is the default format. "exportData": ["hwids", "tags"], // optional. Data to export. Possible values: "hwids", "push_tokens", "users", "tags", "location", "fcm_keys", "web keys", "ad_identifiers" "tagsList": ["Name", "Level"], // optional. Specifies tags to export. To obtain the specific tags only, the "tags" value should be sent within the "exportData" array or the "exportData" be empty. "includeWithoutTokens": true // optional. Set to true to include users without push tokens in the exported file. Default is false. } ``` Note Please find Segmentation Language reference for writing filter expressions [here](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/). For example, to export all subscribers of a particular app, use the following Filter conditions: ```json { "auth": "yxoPUlwqm…………pIyEX4H", // API access token from Pushwoosh Control Panel "filterExpression": "A(\"AAAAA-BBBBB\")", // Filter expression referencing app segment "applicationCode": "AAAAA-BBBBB" // Required Pushwoosh app code } ``` Caution In the response, you’ll get the **`task_id`** to get the resulting file. Then, call [/exportSegment/result](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment-results) with that `task_id` in the request body to retrieve the resulting file. ## exportSegment results `POST` `https://api.pushwoosh.com/api/v2/audience/exportSegment/result` **Retrieves the link to the CSV with the /exportSegment results.** **Request Body** | Name | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | String | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | task_id* | String | Identifier received in your `/exportSegment` response. | * 200: OK ```json { "devicesCount": "24735", "filename": "https://static.pushwoosh.com/segment-export/export_segment_XXXXX_XXXXX_xxxxxxxxxxxxxxxxx.csv.zip", "status": "completed" } ``` Pass the “**task_id**” received in your `/exportSegment` response in the `/exportSegment/result` request body. In the `/exportSegment/result` response, you’ll receive the “filename” parameter. Follow the link provided in that parameter value to automatically download a ZIP archive. Unpack the archive to retrieve the CSV or JSON file (depending on the “format” specified in your request) containing the devices’ data. Starting April 3, 2025 authorization is required to download the file: * If downloading via a **browser**, simply log in to the **Pushwoosh Control Panel** to gain access. * If downloading via **server software**, include the following header in your request: `Authorization: Token YOUR_API_TOKEN` If you specify the “exportData” in your `/exportSegment` request, the file downloaded will contain the data requested only. By default, the file contains the following user data: | **Field** | Description | Example of value | | ------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ | | Hwid | [Hardware ID of a device](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) | 01D1BA5C-AAAA-0000-BBBB-9B81CD5823C8 | | User ID | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) associating a device with a particular user. If no User ID assigned, the HWID is used. | user8192 | | Push Token | Unique identifier assigned to a device by cloud messaging gateways. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token) | eeeb2fd7…0fc3547 | | Type | Platform type (integer). | 1 | | Type (humanized) | Platform type (string). | iOS | | Age | Value of the default Age tag. | 29 | | ApplicationVersion | Value of the default Application Version tag. | 1.12.0.0 | | City | Value of the default City tag. | us, boston | | TagName | Value of a tag created in your account. | TagValue | ### Ad identifiers export Add `"ad_identifiers"` to `exportData` to get a file formatted for uploading directly as a Google Customer Match or Meta Custom Audience source. This value is opt-in only — it is never included by default, even when `exportData` is omitted. It only takes effect with `format` set to `"csv"`. Requesting it with `"json"` or `"json_each_line"` does not return an error, but the columns below are silently omitted from those formats. | **Field** | Description | | ------------ | ------------------------------------------------------------------------ | | MADID | Mobile advertising ID (GAID or IDFA), normalized to lowercase. | | Email SHA256 | SHA-256 hash of the user’s email, lowercased and trimmed before hashing. | | Phone SHA256 | SHA-256 hash of the user’s phone number in E.164 format before hashing. | A row for a user with none of the three identifiers is still exported, with the `MADID`, `Email SHA256`, and `Phone SHA256` columns left empty. ## Export per-user app activity Note [Campaign statistics](https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/) return open/send counts aggregated by hour and platform. There’s no `hwid` or `user_id` column. To see which users opened the app and when, export a segment built on the `PW_ApplicationOpen` event instead. `PW_ApplicationOpen` is mobile-only. For a web project the segment export returns no rows, since the event never fires there. 1. Build a filter expression on the [`PW_ApplicationOpen`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_applicationopen) event, scoped to the period you need. Use the [event date operators](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/#event-date), for example “opened yesterday”: ```plaintext Event("AAAAA-BBBBB", "PW_ApplicationOpen", date daysago eq 1) ``` 2. Call [`/exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment) with that `filterExpression`, `applicationCode`, and `exportData: ["hwids", "users"]`. 3. Call [`/exportSegment/result`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment-results) with the returned `task_id` to download the CSV with `Hwid` and `User ID` columns for every device that opened the app in that window. Caution `User ID` holds whatever identifier you assign a device. If you don’t assign one, it falls back to the HWID. The export tells you who opened the app within the filtered window. It isn’t a per-open event log with a timestamp column, so build one segment per day (or window) if you need day-by-day granularity. --- # Segmentation language **URL:** https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/ Create precise, behavior-based user segments with Pushwoosh Segmentation Language for personalized, targeted messaging. --- Pushwoosh offers a powerful segmentation engine for building granular Segments based on Tag values. **Segmentation Language** is a specific way of writing and combining segmentation criteria to describe a particular group of users that match those criteria and treat them as a single audience segment. This article describes the basic concepts and syntax of the Segmentation Language and provides comprehensive examples of building segmentation criteria for various cases. ## Inline use without a saved segment A filter expression doesn’t have to belong to a saved Segment. Pass one directly in the `expression` field of [`NotifySegment`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifysegment) to target a single send — no segment is created, and the expression isn’t stored on the account. ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "segment": { "application": "XXXXX-XXXXX", "platforms": ["IOS", "ANDROID"], "expression": "AT(\"XXXXX-XXXXX\", \"City\", eq, \"Tokyo\") * AT(\"XXXXX-XXXXX\", \"Age\", gte, 18)", "payload": { "content": { "localized_content": { "en": { "ios": { "body": "Hello, Tokyo!" } } } } }, "schedule": { "at": "2026-05-01T12:00:00Z" } } }' ``` Note Inline expressions are evaluated at send time and skip precompilation — they don’t work with [high-speed delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/), which requires a saved segment referenced by `code`. ## Basics Each device in your user base has specific attributes associated with it via **Tag values**. For example, let’s say a user named Jane lives in Tokyo, and her age is 28. That user will have the following Tags set for their device: * Name: Jane * City: Tokyo * Age: 28 To target Jane, you will need to describe a segment as follows: `AT("XXXXX-XXXXX", "Name", eq, "Jane") * AT("XXXXX-XXXXX", "Age", eq, 28) * AT("XXXXX-XXXXX", "City", eq, "Tokyo")` This combination of conditions is a **filter expression** used in Segmentation Language for describing groups of users aka audience Segments. ## Filter expressions Filter expression is a string that contains a condition or a combination of conditions describing the segment you need. ### Conditions Each condition describes a segment of devices that match the criteria specified in that condition. For example, the following condition builds a segment of users who live in Tokyo: `AT("XXXXX-XXXXX", "City", eq, "Tokyo")` where * AT is an App-scoped tag (condition type); * “XXXXX-XXXXX” is the code of the app the tag value is scoped to; * eq is an operator to apply; * “Tokyo” is a Tag value users have associated with their devices. #### Condition types The following condition types are available for segmentation: * **A** (Application) – describes a segment of devices with a particular app installed. Aliases: **App**, **Application**; * **AT** (App-scoped tag) – describes a segment of devices with the specified Tag values; app code is required as the first argument. Alias: **Tag**; * **Event** – describes a segment of devices triggered a specific Pushwoosh event; * **Geo** – describes a segment of devices within a specific geographic radius; * **BTTS** – describes a segment of devices based on their best time to send; * **Updated** – describes a segment of devices based on their last update timestamp; * **Segment** – references another filter/segment by its code; * **FeatureFlag** – describes a segment of devices by the state of a [feature flag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) they got; Tag conditions require an application code Each custom tag belongs to one application. This is set when the tag is created, and you can’t see it in the Control Panel or in `listTags`. Write tag conditions as `AT("", "tag", ...)` (alias `Tag`) with the code of that application, so Pushwoosh knows where to look up the tag value. A Segment built in the Control Panel doesn’t need this. It’s already bound to one application, so its tag conditions resolve automatically. An expression sent through the API (`expression`/`filter_expression` in [`NotifySegment`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifysegment)) has no such context, so the app code must be explicit. ### Operations with conditions For building sophisticated segments, the following operations can be applied to conditions within the filter expression: #### Union (+) Joins the segments, i.e., builds a new segment of users who match at least one of the specified conditions. For example, to target users who live in Tokyo or in Osaka, you’ll need to describe the segment with the following conditions: `AT("XXXXX-XXXXX", "City", eq, "Tokyo") + AT("XXXXX-XXXXX", "City", eq, "Osaka")` Note Equivalent to logical **disjunction** (**OR**). Can also be written as **or** operator. #### Intersection (*) Constructs a segment of users belonging to both segments described by the conditions. Thus, only those users who comply with each condition you specify will be included. The following expression describes a segment of users who both live in Tokyo and have specified their names: `AT("XXXXX-XXXXX", "City", eq, "Tokyo") * AT("XXXXX-XXXXX", "Name", any)` Note Analog to logical **conjunction** (**AND**). Can also be written as **and** operator. #### Difference (\) Builds a segment of users who belong to one of the described segments and do not belong to another. Users who live in Tokyo but have not given you their names will be described as follows: `AT("XXXXX-XXXXX", "City", eq, "Tokyo") \ AT("XXXXX-XXXXX", "Name", any)` Note Equivalent to logical **negation** (**NOT**). Can also be written as **not** operator. #### Parentheses Determine the order of operations to perform with the conditions in your filter expression. For example, the following filter expression will, at first, obtain a segment of the 12345-67890 subscribers whose age is 18 and then subtract all men of that segment: `( A("12345-67890") * AT("12345-67890", "Age", eq, 18) ) \ AT("12345-67890", "Gender", eq, "Male")` ## Tag conditions operators A field of an [object tag](https://docs.pushwoosh.com/developer/api-reference/object-tags/#segment-by-fields) is a tag with a dotted name, such as `AT("XXXXX-XXXXX", "order.amount", gte, 100)`, and takes the operators of its type. For each Tag type, their own operators are applied. ### Integer Tags Operators * **eq** - equal to a specified value * **noteq** - not equal to a specified value * **lte** - less than or equal to a specified value * **gte** - greater than or equal to a specified value * **in** - any of specified values * **notin** - not equal to any of specified values * **between** - in a specified range * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag Integer Tags syntax example `AT("XXXXX-XXXXX", "int", eq, 42)` ### String Tags Operators * **eq** - equal to a specified value * **noteq** - not equal to a specified value * **startswith** - starts with a specified prefix * **endswith** - ends with a specified suffix * **contains** - contains a specified substring * **in** - equal to any of specified values * **notin** - not equal to any of specified values * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag String Tags syntax examples * `AT("XXXXX-XXXXX", "str", eq, "kangaroo")` - exact match * `AT("XXXXX-XXXXX", "str", startswith, "kang")` - starts with “kang” * `AT("XXXXX-XXXXX", "str", endswith, "roo")` - ends with “roo” * `AT("XXXXX-XXXXX", "str", contains, "nga")` - contains “nga” anywhere in the string ### List Tags Operators * **in** - devices with any of the specified tag values * **notin** - none of the specified tag values associated with the device * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag List Tags syntax example `AT("XXXXX-XXXXX", "list", notin, ["kangaroo", "raccoon"])` ### Date Tags Operators * **eq** - equal to the date specified * **noteq** - not equal to the date specified * **lte** - before or on the date specified * **gte** - after or on the date specified * **in** - equal to any of specified dates * **notin** - not equal to any of specified dates * **between** - in a specified range * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag * **match** - matches a specified month of the year and day of the month. match operator examples * `AT("XXXXX-XXXXX", "Date tag", match month 4 day 1)` - devices that have the “Date tag” set to the 1st of April of any year. * `AT("XXXXX-XXXXX", "Date tag", match month "now" day 13)` - devices that have the “Date tag” set to the 13th day of the current month. * `AT("XXXXX-XXXXX", "Date tag", match month "now" day "now+2")` - devices that have the “Date tag” set to the day after tomorrow. Common use case: birthday greetings. Match operator examples for anniversary and birthday segments You can [segment users based on recurring calendar dates](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments), such as birthdays or anniversaries. In these cases, only the month and day are taken into account, while the year is ignored. * `AT("XXXXX-XXXXX", "birthday", match month "now" day "now")` **Targets users whose birthday is today** (same day and month as today). *Use this to send birthday greetings or special offers on the user’s actual birthday.* * `AT("XXXXX-XXXXX", "birthday", match month "now" day "now+N")` **Targets users whose birthday is in N days** (same month and day, N days from today). *Use this to prepare users in advance, for example, send countdown messages or upcoming event reminders.* * `AT("XXXXX-XXXXX", "birthday", match month "now" day "now-N")` **Targets users whose birthday was N days ago**. *Use this for belated birthday messages or follow-ups like feedback requests.* * `AT("XXXXX-XXXXX", "birthday", match month "now" day N)` **Targets users whose birthday is on the Nth day of the current month**, regardless of the year. *Use this when planning fixed monthly campaigns tied to specific dates (e.g., monthly loyalty rewards on the 10th).* - **daysago eq** - equal to a specified number of days before the present day - **daysago noteq** - not equal to a specified number of days before the present day - **daysago lte** - less than or equal to a specified number of days before the present day - **daysago gte** - greater than or equal to specified number of days before the present day - **daysago between** - between the specified numbers of days daysago operator examples * `AT("XXXXX-XXXXX", "Date tag", daysago, eq, 0)` - the present day from 00:00 to the current moment * `AT("XXXXX-XXXXX", "Date tag", daysago, eq, 1)` - from 00:00 of the previous day to 00:00 of the present day * `AT("XXXXX-XXXXX", "Date tag", daysago, gte, 1)` - all the time to 00:00 of the present day * `AT("XXXXX-XXXXX", "Date tag", daysago, lte, 7)` - from 00:00 of the day 6 days ago to the current moment * `AT("XXXXX-XXXXX", "Date tag", daysago, between, [2,7])` - from 7 to 2 days ago. - **minutesago lte** - less than or equal to a specified number of minutes before the present moment - **minutesago gte** - greater than or equal to a specified number of minutes before the present moment minutesago operator examples * `AT("XXXXX-XXXXX", "Date tag", minutesago, lte, 30)` - the last 30 minutes; * `AT("XXXXX-XXXXX", "Date tag", minutesago, gte, 60)` - 60 or more minutes ago; * `AT("XXXXX-XXXXX", "Date tag", minutesago, lte, 1)` - within the last minute. - **daysahead** - in N to M days from the present day (UTC), inclusive on both ends daysahead operator examples `daysahead` takes a `[from, to]` interval (days from today’s UTC midnight, both ends inclusive). Unlike `daysago`, it has no sub-operator — only the interval form. * `AT("XXXXX-XXXXX", "Date tag", daysahead, [0, 0])` - the present day from 00:00 to 23:59:59 * `AT("XXXXX-XXXXX", "Date tag", daysahead, [1, 1])` - the next day from 00:00 to 23:59:59 * `AT("XXXXX-XXXXX", "Date tag", daysahead, [2, 5])` - any moment from the start of 2 days from now to the end of 5 days from now date operator example * `AT("XXXXX-XXXXX", "Date tag", eq, "2022-12-05 00:00:00")` ### Boolean Tags Operators * **eq** - equal to a specified value * **noteq** - not equal to a specified value * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag Boolean Tags syntax example `AT("XXXXX-XXXXX", "bool", eq, true)` ### Price Tags Operators * **eq** - equal to a specified value * **noteq** - not equal to a specified value * **lte** - less than or equal to a specified value * **gte** - greater than or equal to a specified value * **in** - equal to any of specified values * **notin** - not equal to any of specified values * **between** - in a specified range * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag Price Tags syntax example `AT("XXXXX-XXXXX", "price", between, ["4.2", "6.9"])` ### Version Tags Operators * **eq** - equal to a specified value * **noteq** - not equal to a specified value * **lte** - less than or equal to a specified value * **gte** - greater than or equal to a specified value * **in** - equal to any of specified values * **notin** - not equal to any of specified values * **between** - in a specified range * **any** - devices with any values set for the tag * **notset** - devices with no value set for the tag Version Tags syntax example `AT("XXXXX-XXXXX", "version", lte, "4.2")` ## Event conditions operators Note Please keep in mind the event conditions are app-specific so the app code should be specified in your filter expressions along with the event name. ### Event count * **count gte** - devices triggered the event more than or exactly n times * **count lte** - devices triggered the event less than or exactly n times * **count eq** - devices triggered the event exactly n times * **count noteq** - devices triggered the event any times but not equal to n Event count syntax example `Event("11111-11111", "Level reached", count gte 10) // 11111-11111 is an app code` ### Event date * **date gte** - devices triggered the event later than or exactly on the specified date * **date lte** - devices triggered the event earlier than or exactly on the specified date * **date eq** - devices triggered the event exactly on the specified date * **date noteq** - devices triggered the event whenever but not on the specified date * **date in** - devices triggered the event on any of the specified dates * **date notin** - devices triggered the event whenever but not on any of the specified dates * **date between** - devices triggered the event within a specified period * **date daysago eq** - event trigger date is equal to a specified number of days before the present day * **date daysago noteq** - event trigger date is not equal to a specified number of days before the present day * **date daysago lte** - event trigger date is less than or equal to a specified number of days before the present day * **date daysago gte** - event trigger date is greater than or equal to specified number of days before the present day * **date daysago between** - event trigger date is between the specified numbers of days * **date daysahead** - event trigger date is in a specified number of days from the present day (interval is inclusive on both ends, see Date Tags Operators above for full semantics) * **date minutesago lte** - event trigger date is less than or equal to specified number of minutes before the present moment * **date minutesago gte** - event trigger date is greater than or equal to specified number of minutes before the present moment Event date syntax examples * `Event("11111-11111", "Level reached", date gte "2022-01-01 01:02:03")` * `Event("11111-11111", "Level reached", date in ["2022-01-01 01:02:03", "2022-01-01 02:03:04"])` * `Event("11111-11111", "Level reached", date between "2022-01-01 01:02:03" "2022-01-01 02:03:04")` * `Event("11111-11111", "Level reached", date daysahead 0 7)` - events with a trigger date within the next 7 days * `Event("11111-11111", "Level reached", date minutesago lte 30)` - events within last 30 minutes * `Event("11111-11111", "Level reached", date minutesago gte 60)` - events 60+ minutes ago ### Event platforms Filter events by the platform where they were triggered. * **platforms** - list of platforms (e.g., [“ios”, “android”]) Event platforms syntax example `Event("11111-11111", "Level reached", platforms ["android", "ios"], count gte 5)` ### Event attributes Event conditions can filter based on event attribute values. Attributes support various data types with their corresponding operators. #### Integer Event Attributes * **attribute “name” eq** - attribute equals a specified value * **attribute “name” noteq** - attribute not equal to a specified value * **attribute “name” gte** - attribute greater than or equal to a specified value * **attribute “name” lte** - attribute less than or equal to a specified value * **attribute “name” between** - attribute in a specified range * **attribute “name” in** - attribute equals any of specified values * **attribute “name” notin** - attribute not equal to any of specified values * **attribute “name” any** - any value set for the attribute * **attribute “name” notset** - no value set for the attribute Integer attribute syntax examples * `Event("11111-11111", "Purchase", attribute "amount" eq 42)` * `Event("11111-11111", "Purchase", attribute "amount" between 10 100)` * `Event("11111-11111", "Purchase", attribute "amount" in [10, 20, 30])` #### String Event Attributes * **attribute “name” eq** - attribute equals a specified value * **attribute “name” noteq** - attribute not equal to a specified value * **attribute “name” startswith** - attribute starts with a specified prefix * **attribute “name” endswith** - attribute ends with a specified suffix * **attribute “name” contains** - attribute contains a specified substring * **attribute “name” in** - attribute equals any of specified values * **attribute “name” notin** - attribute not equal to any of specified values * **attribute “name” any** - any value set for the attribute * **attribute “name” notset** - no value set for the attribute String attribute syntax examples * `Event("11111-11111", "Page View", attribute "url" startswith "https://example.com")` * `Event("11111-11111", "Page View", attribute "url" endswith ".html")` * `Event("11111-11111", "Page View", attribute "url" contains "/products/")` * `Event("11111-11111", "Button Click", attribute "button_name" in ["submit", "cancel"])` #### Boolean Event Attributes * **attribute “name” eq** - attribute equals true or false * **attribute “name” noteq** - attribute not equal to true or false Boolean attribute syntax example `Event("11111-11111", "Feature Toggle", attribute "enabled" eq true)` #### Date Event Attributes * **attribute “name” eq** - attribute equals a specified date * **attribute “name” noteq** - attribute not equal to a specified date * **attribute “name” gte** - attribute after or on a specified date * **attribute “name” lte** - attribute before or on a specified date * **attribute “name” between** - attribute within a specified date range * **attribute “name” in** - attribute equals any of specified dates * **attribute “name” notin** - attribute not equal to any of specified dates * **attribute “name” daysago eq/noteq/gte/lte/between** - attribute relative to days ago * **attribute “name” daysahead from to** - attribute in a specified interval of days from the present day (interval is inclusive on both ends, see Date Tags Operators above for full semantics) * **attribute “name” minutesago gte/lte** - attribute relative to minutes ago * **attribute “name” any** - any value set for the attribute * **attribute “name” notset** - no value set for the attribute Date attribute syntax examples * `Event("11111-11111", "Subscription", attribute "start_date" eq "2022-01-01 01:02:03")` * `Event("11111-11111", "Subscription", attribute "start_date" between "2022-01-01 00:00:00" "2022-12-31 23:59:59")` * `Event("11111-11111", "Subscription", attribute "start_date" daysago lte 30)` - within last 30 days * `Event("11111-11111", "Subscription", attribute "renewal_at" daysahead 0 30)` - renewals in the next 30 days * `Event("11111-11111", "Subscription", attribute "start_date" minutesago gte 60)` - 60+ minutes ago #### Price Event Attributes * **attribute “name” eq** - attribute equals a specified price value * **attribute “name” noteq** - attribute not equal to a specified price value * **attribute “name” gte** - attribute greater than or equal to a specified price * **attribute “name” lte** - attribute less than or equal to a specified price * **attribute “name” between** - attribute in a specified price range * **attribute “name” in** - attribute equals any of specified prices * **attribute “name” notin** - attribute not equal to any of specified prices * **attribute “name” any** - any value set for the attribute * **attribute “name” notset** - no value set for the attribute Price attribute syntax example `Event("11111-11111", "Purchase", attribute "total_price" between 10.00 50.00)` #### List Event Attributes * **attribute “name” in** - attribute contains any of specified values * **attribute “name” notin** - attribute doesn’t contain any of specified values * **attribute “name” any** - any value set for the attribute * **attribute “name” notset** - no value set for the attribute List attribute syntax example `Event("11111-11111", "Cart Update", attribute "product_ids" in ["prod-123", "prod-456"])` ### Event attribute sums A sum option compares the total of an integer or price attribute across all the events that pass the other options of the same `Event(...)` condition: count, date, platforms, and attribute conditions. * **sum “name” eq** - the total equals a specified value * **sum “name” noteq** - the total is not equal to a specified value * **sum “name” gte** - the total is greater than or equal to a specified value * **sum “name” lte** - the total is less than or equal to a specified value * **sum “name” between** - the total is in a specified range, inclusive on both ends Rules: * The attribute must be of the integer or price type. Any other type makes the expression invalid. * For an integer attribute, the value must be a whole number. `sum "qty" gte 1.5` is invalid. * For `between`, the first value must be less than or equal to the second. * Values can be negative. A negative attribute value, such as a refund, is subtracted from the total. * An event without the attribute adds 0 to the total. * A device with no events passing the other options doesn’t match the condition, whatever the sum value. * With `count eq 0`, the condition matches all devices except those whose events pass the other options and the sums. * Several sum options in one condition are combined with AND. The order of options inside `Event(...)` doesn’t matter. Event attribute sum syntax examples * `Event("11111-11111", "Purchased", sum "amount" gte 100)` - spent 100 or more in total * `Event("11111-11111", "Purchased", sum "amount" between 0.5 99.99)` * `Event("11111-11111", "Purchased", sum "amount" lte -1)` - refunds exceed purchases * `Event("11111-11111", "Purchased", date daysago lte 90, count gte 2, attribute "currency" eq "USD", sum "amount" gte 100, sum "qty" lte 10)` ## Additional condition types ### Geo condition Target devices based on geographic location within a specified radius. **Syntax:** `Geo("", , , )` Geo syntax example `Geo("11111-11111", 53.2734, -7.77832031, 100.05)` - devices within 100.05 km of coordinates ### BTTS condition Target devices by their best time to send notifications (hour of day, 0-23). **Operators:** * **any** - any best time to send value set * **eq** - exact hour match * **noteq** - not equal to hour * **gte** - hour greater than or equal to * **lte** - hour less than or equal to BTTS syntax examples * `BTTS("11111-11111", eq 10)` - devices with best time at 10:00 * `BTTS("11111-11111", gte 18)` - devices with best time from 18:00 onwards * `BTTS("11111-11111", any)` - devices with any best time value ### Updated condition Filter devices by their last update timestamp. **Operators:** * **gte** - updated after or on specified date * **lte** - updated before or on specified date * **between** - updated within date range Updated syntax examples * `Updated("11111-11111", gte "2022-01-01 00:00:00")` - devices updated after Jan 1, 2022 * `Updated("11111-11111", lte "2022-12-31 23:59:59")` - devices updated before Dec 31, 2022 * `Updated("11111-11111", between "2022-01-01 00:00:00" "2022-12-31 23:59:59")` - devices updated in 2022 ### Segment condition Reference another filter/segment by its code. Segment syntax example `Segment("11111-11111", "22222-22222")` - reference filter with code “22222-22222” ### Feature flag condition Target devices by the result of a feature flag they got from the SDK. **Syntax:** `FeatureFlag("", "", "")` The state takes one of these values: * `enabled` - devices that got the flag on * `disabled` - devices that got the flag off * `rule:` - devices that matched the segment of this rule, whether its rollout turned the flag on or not. Use a `rule_id` from the [Feature Flags API](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/#rule-object). FeatureFlag syntax examples * `FeatureFlag("11111-11111", "new_checkout", "enabled")` - devices with the flag `new_checkout` on * `FeatureFlag("11111-11111", "new_checkout", "disabled")` - devices with the flag off * `FeatureFlag("11111-11111", "new_checkout", "rule:r_1a2b3c4d")` - devices that matched the rule `r_1a2b3c4d` A device that has never requested the flag matches none of the states. When the flag is killed or archived, `enabled` and `rule:` match no devices within about 10 seconds, and `disabled` matches every device that has ever got the flag. The flag and the rule must exist in the application, and the account’s plan must include feature flags. Otherwise the segment isn’t saved, and the error names the missing flag or rule or says `Your plan does not include feature flags`. Where the API returns a reason code, it’s `FILTER_EXPRESSION_FEATURE_FLAG_NOT_FOUND`. A High Speed Delivery segment, which can hold only tag conditions, can’t contain this condition. ## Application condition device presence flags Application conditions support additional flags to control device and token filtering: * **with_tokens** - devices with push notification tokens * **without_tokens** - devices without push notification tokens * **with_devices** - users with registered devices * **without_devices** - users/profiles without registered devices Application flags syntax examples * `A("11111-11111", ["ios", "android"], [with_tokens])` - devices with push tokens * `A("11111-11111", ["ios", "android"], [without_tokens])` - devices without push tokens * `A("11111-11111", [], [with_devices])` - all devices with device records * `A("11111-11111", ["ios", "android"], [without_tokens, with_devices])` - devices without tokens but with device records ## Filter expression examples Note When writing JSON queries, be sure to escape quotes (`"`) and backslashes (`\`) with a backslash (`\`). For an example request, see [/exportSegment](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment). ### Basic examples 1. iOS and Android devices with the app installed and with push tokens: ```json A("11111-11111", ["ios","android"], [with_tokens]) ``` 2. iOS and Android devices with the app installed but without push tokens: ```json A("11111-11111", ["ios","android"], [without_tokens]) ``` 3. iOS and Android devices with the app installed, whether they have push tokens or not: ```json A("11111-11111", ["ios","android"], [with_tokens, without_tokens]) ``` 4. All app subscribers who have purchased something in the app: ```json AT("11111-11111", "In-App Purchase", gte, 1) ``` ### Advanced examples 5. Users in Tokyo who have opened the app in the last 7 days: ```json AT("11111-11111", "City", eq, "Tokyo") * Event("11111-11111", "App Open", date daysago lte 7) ``` 6. Users who made a purchase over $50 in the last 30 days: ```json Event("11111-11111", "Purchase", attribute "total_price" gte 50.00, date daysago lte 30) ``` 7. Users whose names start with “J” or end with “e”: ```json AT("11111-11111", "Name", startswith, "J") + AT("11111-11111", "Name", endswith, "e") ``` 8. Active iOS users within 100km of New York who haven’t purchased recently: ```json A("11111-11111", ["ios"], [with_tokens]) * Geo("11111-11111", 40.7128, -74.0060, 100) \ Event("11111-11111", "Purchase", date daysago lte 30) ``` 9. Users with best time to send between 9 AM and 5 PM: ```json BTTS("11111-11111", gte 9) * BTTS("11111-11111", lte 17) ``` 10. Users who triggered a specific event on Android or iOS in the last hour: ```json Event("11111-11111", "Button Click", platforms ["android", "ios"], date minutesago lte 60) ``` 11. Devices updated in the last 3 months with app version 4.2 or higher: ```json Updated("11111-11111", gte "2024-07-01 00:00:00") * AT("11111-11111", "App Version", gte, "4.2") ``` --- # SMS API **URL:** https://docs.pushwoosh.com/developer/api-reference/sms/ Send SMS notifications to users using API methods for customer communication and engagement. --- /createSMSMessage is deprecated New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — pass `platforms: ["SMS"]` to `Notify`. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/#from-createsmsmessage). Note Before sending SMS messages, connect your SMS provider in the Control Panel. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/) Caution `/createSMSMessage` doesn’t support MMS. If `preset` points at an [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) with [MMS attachments](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-mms-attachments), this method ignores the subject and images and sends only the plain-text body. To send MMS, use [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#mms) instead. ## createSMSMessage Deprecated Used to send SMS notifications to users POST `https://api.pushwoosh.com/json/1.3/createSMSMessage` ### Request body | Name | Required | Type | Description | | ------------------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------ | | **auth*** | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | **application*** | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | **notifications*** | Yes | array | Content settings. JSON array of message parameters. See details below. | **Notification parameters** | Name | Required | Type | Description | | ---------------------------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **send_date*** | Yes | string | Date and time to send the notification. Use format `YYYY-MM-DD HH:mm` or `now` to send immediately. | | **content*** | Yes | string | Text content of the SMS message. | | **devices*** | Yes | array | Customer phone number (must be associated with a UserId using `/registerDevice` and specified in the `hwid` parameter or use `use_auto_registration`). Only one number can be specified here. | | **use_auto_registration** | No | boolean | If set to `true` the phone number specified in the `devices` parameter will be automatically registered. | | **dynamic_content_placeholders** | No | object | Dynamic content placeholders to customize the message content. Each placeholder will be replaced with corresponding dynamic values. | | **preset** | No | string | SMS [Preset Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#preset-code) from your Control Panel. | ### Request example ```json { "request": { "application": "12345-67891", // required. Pushwoosh application code. "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "notifications": [{ "send_date": "now", // required. YYYY-MM-DD HH:mm OR "now". "content": "Hello!", // required. SMS text (string). "devices": ["+1234567890"], // required. Customer phone number (must be associated with // a UserId using. /registerDevice and specified in // the "hwid" parameter or use "use_auto_registration"). // Only one number can be specified here. "preset": "XXXXX-XXXXX", // optional. SMS Preset Code from your Control Panel. "use_auto_registration": true // optional. Automatically register phone number specified // in "devices" parameter. }] } } ``` ### Response example ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": [ "9648-0B10ECB6-0D9F214D" ] } } ``` ### Error response ```json { "status_code": 210, "status_message": "Invalid devices list. \"devices\" must be an array.", "response": { "Messages": [] } } ``` Note Currently, each SMS must be sent in a separate request for each customer. To get SMS statistics, use the [totalsByIntervals](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#totalsbyintervals) method. You can also view statistics for each message in the [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) section. Caution Please note that some mobile operators do not transmit delivery status data, so the statistics may need to be more accurate. You can check the details with the mobile operators that provide numbers to your customers. --- # SMTP gateway **URL:** https://docs.pushwoosh.com/developer/api-reference/smtp-gateway/ Send Pushwoosh transactional email through any SMTP client. Authenticate with your application code and API token; the gateway forwards every message to Messaging API v2. --- The SMTP gateway accepts standard mail submission and forwards each message to [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) as a transactional email. Use it when an existing mail tool — an MTA, a framework mailer, an SDK — is easier to plug in than a JSON request to the API. Note The gateway is transactional-only. For audience segments, scheduling, or A/B campaigns, call [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) directly. ## How it works ```plaintext any SMTP client smtp gateway Messaging API v2 ──────────────── ──────> ──────────────── ──────> ───────────────── submission STARTTLS gRPC Notify AUTH PLAIN + AUTH PLAIN Authorization: Token ``` 1. The client connects to `smtp.pushwoosh.com` on port `587`, upgrades the connection to TLS with `STARTTLS`, then authenticates with `AUTH PLAIN`. 2. The gateway parses the MIME message and builds a `Notify` request with `platforms: ["EMAIL"]` and `message_type: TRANSACTIONAL`. 3. The API token from `AUTH PLAIN` is forwarded to Messaging API as the `Authorization` header. Token validation, application matching, sending identity, and bounce handling all happen on the API side. ## Endpoint | Setting | Value | | ------- | ------------------------------------ | | Host | `smtp.pushwoosh.com` | | Port | `587` (SMTP submission) | | TLS | `STARTTLS` — mandatory before `AUTH` | | Auth | `AUTH PLAIN` | ## Authentication `AUTH PLAIN` uses two Pushwoosh credentials. | AUTH field | Pushwoosh value | | ---------- | --------------------------------------------------------------------------------------------------------- | | `username` | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code), for example `XXXXX-XXXXX` | | `password` | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) | `AUTH` is rejected outside TLS. The token never appears in the message — it is only used to authorize the upstream `Notify` call. ## How messages map to Notify | MIME or SMTP field | Notify field | | ------------------ | ----------------------------------------------------------------------------------- | | `RCPT TO` | `target.users.list` — Pushwoosh resolves these addresses to subscribers | | AUTH `username` | `application` | | `Subject:` header | `email_payload.subject["default"]` (RFC 2047 decoded) | | `From:` header | `email_payload.from` — `name` and `email` | | HTML part | `email_payload.body` (preferred when both parts are present) | | Plain-text part | `email_payload.body` (used when HTML is absent) | | `MAIL FROM` | Ignored — Pushwoosh substitutes its own sending identity and handles bounces itself | Every message is sent with `schedule.send_date: now`. ## Limits | Limit | Value | | ------------------------------------------- | ------ | | Maximum message size | 25 MiB | | Maximum recipients per envelope (`RCPT TO`) | 50 | ## Error mapping gRPC status codes returned by Messaging API are translated to standard SMTP reply codes so that any SMTP client surfaces a meaningful error. | Upstream gRPC status | SMTP reply | Meaning | | ------------------------------------------------------- | ----------- | ------------------------------------------------------- | | `Unauthenticated` | `535 5.7.8` | Bad application code or API token. | | `PermissionDenied` | `550 5.7.1` | The token has no rights for this application. | | `InvalidArgument` / `FailedPrecondition` / `OutOfRange` | `550 5.6.0` | Bad MIME content (for example missing subject or body). | | `NotFound` | `550 5.1.1` | Application or recipient was not found. | | `ResourceExhausted` | `452 4.5.3` | Rate limit reached — retry later. | | `DeadlineExceeded` / `Unavailable` | `451 4.4.1` | Transient upstream error — retry later. | | any other failure | `451 4.5.0` | Transient internal error — retry later. | Codes in the `4xx` range are temporary and should be retried by the client; codes in the `5xx` range are permanent and require a client-side fix. ## Example: send with swaks ```bash swaks --server smtp.pushwoosh.com:587 \ --auth-user "XXXXX-XXXXX" \ --auth-password "YOUR_API_TOKEN" \ --tls \ --from from@example.com \ --to user@example.com \ --header "Subject: Hello from SMTP gateway" \ --body "Plain-text body" ``` The `From:` header in the MIME body is what reaches Pushwoosh — the `--from` envelope (`MAIL FROM`) is discarded. ## Notes * The gateway is stateless and does not store messages. Once forwarded, delivery is owned by Messaging API. * Bounces, complaints, and unsubscribe links are handled by Pushwoosh, the same as for any other transactional email. * For campaign sending (segments, scheduling, A/B), use [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) directly — the SMTP gateway is submission-only. ## See also [Messaging API v2 overview ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) [Notify ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) [Email payload reference ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/email-payload-reference/) [Marketing vs transactional ](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) --- # Application and subscriber statistics **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/ Retrieve and analyze your app statistics and subscriber metrics, device types, and engagement data for insights --- These methods return statistics for a whole Pushwoosh application rather than for a single message. Use them to get the application’s activity for a time period and to count its subscribers by platform. ## getAppStats Get the statistics of a specific app for a defined time period. `POST` `https://api.pushwoosh.com/json/1.3/getAppStats` ##### Request body parameters | Name | Required | Type | Description | | ------------- | -------- | ------ | -------------------------------------------------------------------------------------------- | | `auth` | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | | `application` | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `datefrom` | Yes | string | Start date and time of the reporting period. Format: `Y-m-d H:i:s`. | | `dateto` | Yes | string | End date and time of the reporting period. Format: `Y-m-d H:i:s`. | ##### Request example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "datefrom": "2013-06-04 00:00:00", // required. Date and time, start of the reporting period "dateto": "2013-06-07 00:00:00" // required. Date and time, end of the reporting period } } ``` ##### Response example ```json { "status_code": 200, "status_message": "OK", "response": { "request_id": "c93a202f439235f9adaaa06d651548ab" } } ``` ### Understanding the statistics Statistics display registered actions for an application, device, or message within the specified timeframe. Reports are automatically aggregated using the following rules: * **Yearly**: If the period is longer than one year. * **Monthly**: If the period is longer than one month. * **Daily**: If the period is longer than one day. * **Hourly**: If the period is longer than three hours. * **Minutely**: In all other cases. ##### Action types * **Application Level**: `_open_`, `_install_` * **Device Level**: `_register_`, `_unregister_` * **Message Level**: `_send_`, `_open_` ##### Response format All statistics objects have the same format: | Field | Type | Description | | ----------- | ------ | ------------------------------------------------------- | | `formatter` | string | Report scale: yearly, monthly, daily, hourly, minutely. | | `rows` | list | Contains report data for each registered action. | Each report row contains: | Field | Type | Description | | ---------- | ------ | ------------------------------ | | `count` | int | Number of registered actions. | | `action` | string | The type of registered action. | | `datetime` | string | Formatted date: `Y-m-d H:i:s`. | ### Retrieving scheduled request results Important As with every scheduled request, `/getAppStats` requires an additional [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) request. ##### Response body | Field | Type | Description | | ------------ | ------ | ------------------------------------------------------------------------------------------------------------------------ | | `request_id` | string | Scheduled request ID. Refer to [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) for more details. | ##### Scheduled (/getResults) response body | Field | Type | Description | | -------------- | ---------- | ---------------------------- | | `applications` | dictionary | Statistics for applications. | | `devices` | dictionary | Statistics for devices. | | `messages` | dictionary | Statistics for messages. | ##### Example ```json { "error": { "code": 0, "message": "OK" }, "json_data": { "applications": { "formatter": "hourly", "rows": [{ "count": 0, "action": "open", "datetime": "2013-06-06 00:00:00" }, { ... }] } } } ``` ## getApplicationSubscribersStats Displays the app’s subscribers list grouped by the types of their devices. `POST` `https://api.pushwoosh.com/json/1.3/getApplicationSubscribersStats` ##### Request Body | Name | Required | Type | Description | | ------------- | -------- | ------ | -------------------------------------------------------------------------------------------- | | `auth` | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | | `application` | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | **Request example** ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX" // required. Pushwoosh application code } } ``` * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "IOS": 1, "ANDROID": 1, "OSX": 0, "WINDOWS": 0, "AMAZON": 0, "SAFARI": 0, "FIREFOX": 0 } } ``` ## getSubscribersStatistics Retrieves app subscribers stats for a time period. `POST` `https://api.pushwoosh.com/api/v2/statistics/application/getSubscribersStatistics` ##### Headers | Name | Required | Description | | ------------- | -------- | -------------------------------------------------------------------------------------------------- | | Authorization | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) in the format: `Key PKX.......NHg`. | | Content-Type | Yes | Must be set to `application/json`. | ##### Request body parameters | Name | Required | Type | Description | | ----------------- | -------- | ------ | ---------------------------------------------------------------------------------------- | | application_code | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | timestamp_from | Yes | string | Start date and time of the stats period (format: `YYYY-MM-DD hh:mm:ss`, UTC+0). | | timestamp_to | Yes | string | End date and time of the stats period (format: `YYYY-MM-DD hh:mm:ss`, UTC+0). | **Example request** ```shell curl --location --request POST 'https://api.pushwoosh.com/api/v2/statistics/application/getSubscribersStatistics' \ --header 'Authorization: Key 3a2X......828JreCk48f' \ --header 'Content-Type: application/json' \ --data-raw '{ "application_code": "12345-67890", // Pushwoosh app code "timestamp_from": "2022-08-01 00:00:00", // UTC+0 "timestamp_to": "2022-09-01 00:00:00" // UTC+0 }' ``` **Example response** ```json { "statistics": [{ "timestamp": "YYYY-MM-DD hh:mm:ss", // UTC+0 "platform": 1, "push_enabled": 100, "push_disabled": 100 }] } ``` **Response codes** * 200: OK ```json { "statistics": [{ "timestamp": "YYYY-MM-DD hh:mm:ss", "platform": 1, "push_enabled": 100, "push_disabled": 100 }] } ``` **Explanation**: The request was successful, and the statistics are returned. * 400: Bad Request ```json { // Response } ``` **Explanation**: The request had invalid syntax or parameters. * 500: Internal Server Error ```json { // Response } ``` **Explanation**: The server encountered an error. Try again later. * 401: Unauthorized ```json { // Response } ``` **Explanation**: Authentication failed. Check your API key or token. * 403: Forbidden ```json { // Response } ``` **Explanation**: Access denied for the specified app code. * 404: Not Found ```json { // Response } ``` **Explanation**: The app code was not found or doesn’t exist. ### Timestamp interval rules Note Please take into consideration that the intervals between timestamps in the response depend on the period you send in your request as follows: * if you request the stats for a period longer than one year, the stats timestamps interval will be a year * if the stats period equals a year, the interval between response’s timestamps equals a month * for periods longer than a month but less than a year, stats for every day will be returned * for periods less than a month, the response will include stats for every hour | Requested period | Interval in response | | ----------------- | -------------------- | | More than 1 year | 1 year | | 1 year | 1 month | | 1 month - 1 year | 1 day | | Less than 1 month | 1 hour | --- # Campaign statistics **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/ Retrieve detailed campaign statistics in Pushwoosh, including sent, opened, and conversion metrics for a specified time period --- `/getCampaignStats` returns the statistics of one campaign for a time period. Use it to measure the combined performance of the messages sent within a campaign. ## getCampaignStats Retrieve statistics of a particular campaign for a specific time period. `POST` `https://api.pushwoosh.com/json/1.3/getCampaignStats` Tip This method returns counts aggregated by hour and platform, not per-device or per-user data. There’s no `hwid` or `user_id` in the response. To see which users opened the app and when, [export a segment](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#export-per-user-app-activity) built on the `PW_ApplicationOpen` event instead. ##### Request body parameters | Name | Required | Type | Description | | ---------- | -------- | ------ | -------------------------------------------------------------------------------------------- | | `auth` | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | | `campaign` | Yes | string | Unique [campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). | | `datefrom` | Yes | string | Start of the reporting period (formatted as `Y-m-d H:i:s`). | | `dateto` | Yes | string | End of the reporting period (formatted as `Y-m-d H:i:s`). | ##### Example request ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "campaign": "XXXXX-XXXXX", // required. Campaign code "datefrom": "2024-05-01 00:00:00", // required. Start of reporting period "dateto": "2024-05-10 23:59:59" // required. End of reporting period } } ``` ##### Example response ```json { "status_code": 200, "status_message": "OK", "response": { "request_id": "a3ef436445abfdef6255cc2f65ce7614" } } ``` #### Retrieving scheduled request results Important Like every scheduled request, `/getCampaignStats` requires an additional [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) request. Example getResults response ```json { "status_code": 200, "status_message": "OK", "response": { "formatter": "hourly", "rows": [{ "count": 0, "action": "open", "datetime": "2024-05-09 00:00:00", "platformid": 14 }, { "count": 0, "action": "send", "datetime": "2024-05-09 00:00:00", "platformid": 14 }, { "count": 0, "action": "send", "datetime": "2024-05-11 00:00:00", "platformid": 14 }], // conversion (if goal tracking is allowed) "conversion": { "send": 10, "open": 5, "events": [{ "uid": 1, "event": "event name", "hits": 5, "conversion": "100%", "revenue": 15.34 }] } } } ``` --- # Events and tags statistics **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/ Learn how to retrieve event and tag statistics in Pushwoosh, track user interactions, and analyze tag usage with API requests --- These methods return usage statistics for events and tags. Use `/getEventStatistics` to count how many times an event was triggered in a period and `/getTagStats` to get statistics for a single tag. ## getEventStatistics Retrieves the number of times an event was triggered during a specified period. `POST` `https://api.pushwoosh.com/json/1.3/getEventStatistics` ##### Request body parameters | Name | Required | Type | Description | | ------------- | -------- | ------ | ------------------------------------------------------------------------------------------------------------------ | | `auth` | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | | `application` | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `event` | Yes | string | [Event name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#event-name), exactly as created in Pushwoosh Control Panel. | | `date_from` | Yes | string | Start of the period to retrieve statistics for. | | `date_to` | Yes | string | End of the period to retrieve statistics for. | | `attributes` | No | array | An array of event attributes for filtering results. | ##### Example request ```json { "request": { "auth":"yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX", // required. Pushwoosh application code "event": "Event name", // required. The name of Event exactly as created in Pushwoosh Control Panel "date_from": "2024-09-12", // required. Start of the reporting period "date_to": "2024-09-12", // required. End of the reporting period "attributes": [{ // optional. "name": "attributeName", "operator": "EQ", "values": ["val"] }] } } ``` ##### Example response ```json { "status_code": 200, "status_message": "OK", "response": { "request_id": "DC1_bc63f00c736696d75cea17aa6855ba19" } } ``` #### Retrieving scheduled request results Important Like every scheduled request, **/getEventStatistics** requires an additional [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) request. ##### Request body parameters | Field | Type | Description | | ------------ | ------ | ---------------------------------------------------------------------------------------------------------------------- | | `request_id` | string | Scheduled request ID. Use [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) to retrieve results. | Scheduled (/getResults) response ```json { "status_code": 200, "status_message": "OK", "response": [{ "date": "2024-09-12 00:00", "count": 12 }, { "date": "2024-09-12 01:00", "count": 2 }, { "date": "2024-09-12 02:00", "count": 4 }, { "date": "2024-09-12 03:00", "count": 8 }, { "date": "2024-09-12 04:00", "count": 9 }, { "date": "2024-09-12 05:00", "count": 2 }, { "date": "2024-09-12 06:00", "count": 0 }, { "date": "2024-09-12 07:00", "count": 0 }, { "date": "2024-09-12 08:00", "count": 0 }, { "date": "2024-09-12 09:00", "count": 0 }, { "date": "2024-09-12 10:00", "count": 0 }, { "date": "2024-09-12 11:00", "count": 0 }, { "date": "2024-09-12 12:00", "count": 0 }, { "date": "2024-09-12 13:00", "count": 0 }, { "date": "2024-09-12 14:00", "count": 0 }, { "date": "2024-09-12 15:00", "count": 0 }, { "date": "2024-09-12 16:00", "count": 0 }, { "date": "2024-09-12 17:00", "count": 0 }, { "date": "2024-09-12 18:00", "count": 0 }, { "date": "2024-09-12 19:00", "count": 0 }, { "date": "2024-09-12 20:00", "count": 0 }, { "date": "2024-09-12 21:00", "count": 0 }, { "date": "2024-09-12 22:00", "count": 0 }, { "date": "2024-09-12 23:00", "count": 0 }] } ``` ## getTagStats Retrieves statistics for a specific tag. `POST` `https://api.pushwoosh.com/json/1.3/getTagStats` ##### Request body parameters | Name | Required | Type | Description | | -------------- | -------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `auth` | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | | `tag` | Yes | string | [Tag name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag), exactly as in Pushwoosh Control Panel. | | `applications` | No | array | List of [applications](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). Sets the scope of the statistics. With `applications`, you get counts per application. Without it, you get one summary for the whole account. | ##### Example request ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", "tag": "TAG_NAME", "applications": [ "APPLICATION_1", "APPLICATION_2", "APPLICATION_3" ] } } ``` #### Retrieving scheduled request results Important Like every scheduled request, **getTagStats** requires an additional [`/getResults`](https://docs.pushwoosh.com/developer/api-reference/scheduled-requests#getresults) request. ##### `/getResults` response ```json { "status_code": 200, "status_message": "OK", "response": { "fileName": "DIRECT_FILE_URL.csv" } } ``` Note The response file is a CSV with a semicolon (`;`) separator. #### Example CSV content With `applications`, each row is `application code;tag value;count`, grouped by application: ```csv 13C2B-72C62;ua_settingpushbod;3 13C2B-72C62;ua_settingpushhealth;3 13C2B-72C62;ua_settingpushstrength;3 13C2B-72C62;ua_settingpushupdate;2 ``` Without `applications`, each row is `tag value;count` for the whole account, one row per value, sorted by value: ```csv ua_settingpushbod;5 ua_settingpushhealth;4 ua_settingpushstrength;3 ua_settingpushupdate;2 ``` --- # Asynchronous message statistics export **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/ API methods to export message history and statistics as a CSV file in the background, without the row limits of messages:list. --- `exportMessagesStatistics` exports message history and statistics to a CSV file on the server. Use it for large or full-account pulls that [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) can’t handle. ## When to use export instead of messages:list Use `messages:list` for live, paginated lookups of a bounded period. Use `exportMessagesStatistics` when the result would exceed the deep-pagination limit of `messages:list` (`page × per_page > 100000`), or when the goal is a single downloadable file rather than paged JSON. The export has no limit on `date_range` or row count, because it streams the result to a file on disk instead of holding it in one response. ## How the export flow works 1. Call [`export`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#export) with the same filters as `messages:list`. The response returns a `uid` task identifier immediately, before the file is generated. 2. Poll [`status`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#status) with that `uid` until it reports `STATUS_SUCCESS` (or `STATUS_FAILED`). 3. Call [`result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) with the same `uid` to get the generated `file` and a ready-to-use `file_url`. 4. [Download](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#download) the file. Use `file_url` as-is, or see [`result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) for building the URL from `file` if it came back empty. Use [`lastTasks`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#lasttasks) to look up recent export tasks for an application, and [`delete`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#delete) to cancel a task or remove its file early. ## Methods The export lifecycle has five methods, plus a plain download endpoint: | Method | Description | | -------------------------------------------------- | ----------------------------------------------------------------------- | | [`exportMessagesStatistics/export`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#export) | Queues an export and returns a task `uid`. | | [`exportMessagesStatistics/status`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#status) | Checks task progress. | | [`exportMessagesStatistics/result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) | Returns the generated file name once the task is done. | | [`exportMessagesStatistics/lastTasks`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#lasttasks) | Lists recent export tasks for an application. | | [`exportMessagesStatistics/delete`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#delete) | Cancels a task or removes its file before the retention window expires. | | [Download](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#download) | Downloads the generated CSV file by name. | ### export Queues a message history export and returns a task identifier right away. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/export` ##### Headers The request needs a Server API token: | Name | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). Must be provided in the following format: `Authorization: Api `. | ##### Request body parameters The request body accepts the following fields: | Name | Required | Type | Description | | ------------------------------------- | -------- | ------ | ---------------------------------------------------------------------------------------------------------------------------------- | | `type` | Yes | String | Must be `“TASK_TYPE_EXPORT_MESSAGES_V2”`. | | `export_messages_v2` | Yes | Object | Export parameters, described below. | | `export_messages_v2.application_code` | See note | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). Required if `app_group_code` is not set. | | `export_messages_v2.app_group_code` | See note | String | Application group code, exports across every app in the group. Required if `application_code` is not set. | | `export_messages_v2.search` | No | String | Free-text search over message title and content. | | `export_messages_v2.filters` | No | Object | Message filters, described below. Omit to export the whole account history. | | `export_messages_v2.properties` | No | Array | Columns to include in the CSV, described below. | `export_messages_v2.filters` accepts: | Name | Type | Description | | -------------- | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `statuses` | Array | Message statuses to include.Possible values- `”MESSAGE_STATUS_CANCELED"` - `"MESSAGE_STATUS_CREATING"` - `"MESSAGE_STATUS_DONE"` - `"MESSAGE_STATUS_FAIL"` - `"MESSAGE_STATUS_PENDING"` - `"MESSAGE_STATUS_PROCESSING"` - `"MESSAGE_STATUS_WAITING”` | | `platforms` | Array | [Platform codes](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) (numeric, e.g. `1` for iOS), not the platform name strings used by `messages:list`. | | `sent_date` | Object | Reporting period filtered on send date: `{"date_from": "YYYY-MM-DD", "date_to": "YYYY-MM-DD"}`. | | `created_date` | Object | Reporting period filtered on message creation date, same format as `sent_date`. | | `created_via` | Array | Message source.Possible values- `”AB_TEST"` - `"API"` - `"AUTO_PUSH"` - `"CP"` - `"CSV"` - `"CUSTOMER_JOURNEY"` - `"EMAIL_API"` - `"EMAIL_CP"` - `"GEO_ZONE"` - `"PUSH_ON_EVENT"` - `"RSS"` - `"SYSTEM”` | | `segments` | Array | [Filter codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code) the message was sent to. | | `campaigns` | Array | [Campaign codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). Unlike `messages:list`, this takes a list, not a single code. | | `message_id` | String (uint64) | A single numeric message ID, quoted. Unlike `messages:list`, export takes one ID, not an array. | | `message_code` | String | A single [message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code). | `export_messages_v2.properties` selects which columns the CSV contains. Possible values * `"EXPORT_MESSAGE_PROPERTY_ID"` * `"EXPORT_MESSAGE_PROPERTY_TIMESTAMP"` * `"EXPORT_MESSAGE_PROPERTY_CONTENT"` * `"EXPORT_MESSAGE_PROPERTY_TITLE"` * `"EXPORT_MESSAGE_PROPERTY_CAMPAIGN_NAME"` * `"EXPORT_MESSAGE_PROPERTY_APPLICATIONS"` * `"EXPORT_MESSAGE_PROPERTY_STATUS"` * `"EXPORT_MESSAGE_PROPERTY_PLATFORMS"` * `"EXPORT_MESSAGE_PROPERTY_SOURCE"` * `"EXPORT_MESSAGE_PROPERTY_FILTER"` * `"EXPORT_MESSAGE_PROPERTY_SUBSCRIPTION_SEGMENTS"` * `"EXPORT_MESSAGE_PROPERTY_SENT"` * `"EXPORT_MESSAGE_PROPERTY_OPENED"` * `"EXPORT_MESSAGE_PROPERTY_ERRORS"` * `"EXPORT_MESSAGE_PROPERTY_RECIPIENTS"` * `"EXPORT_MESSAGE_PROPERTY_DELIVERED"` * `"EXPORT_MESSAGE_PROPERTY_TOTAL_DELIVERED"` * `"EXPORT_MESSAGE_PROPERTY_TOTAL_OPENED"` * `"EXPORT_MESSAGE_PROPERTY_TOTAL_CLICKS"` * `"EXPORT_MESSAGE_PROPERTY_CLICKS"` * `"EXPORT_MESSAGE_PROPERTY_UNSUBSCRIBED"` properties is not just a filter A property not listed in `properties` does not appear in the file at all, including the base columns (ID, send date, content, status). Leaving `properties` empty produces a CSV with no columns. List every column the export should contain, not only the metrics you want to add on top of a default set. ##### Example request ```json { "type": "TASK_TYPE_EXPORT_MESSAGES_V2", "export_messages_v2": { "application_code": "XXXXX-XXXXX", "filters": { "created_date": { "date_from": "2026-01-01", "date_to": "2026-06-30" }, "statuses": ["MESSAGE_STATUS_DONE"], "platforms": [1, 3] }, "properties": [ "EXPORT_MESSAGE_PROPERTY_ID", "EXPORT_MESSAGE_PROPERTY_TIMESTAMP", "EXPORT_MESSAGE_PROPERTY_STATUS", "EXPORT_MESSAGE_PROPERTY_PLATFORMS", "EXPORT_MESSAGE_PROPERTY_SENT", "EXPORT_MESSAGE_PROPERTY_OPENED" ] } } ``` * 200: OK ```json { "uid": "177458" } ``` * 401: Incorrect API access token ```json { "error": "account not found" } ``` ### status Returns the progress of an export task. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/status` ##### Request body parameters Pass the task identifier returned by `export`: | Name | Required | Type | Description | | ----- | -------- | -------------- | ------------------------------------------------------------ | | `uid` | Yes | String (int64) | Task identifier from the `export` response, e.g. `"177458"`. | ##### Example request ```json { "uid": "177458" } ``` * 200: OK ```json { "status": "STATUS_SUCCESS", "progress": 1 } ``` `status` is one of `"STATUS_PENDING"`, `"STATUS_SUCCESS"`, or `"STATUS_FAILED"`. `progress` is a fraction between `0` and `1`; poll `status` until it reaches `"STATUS_SUCCESS"` before calling `result`. ### result Returns the generated file name once the task has completed. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/result` ##### Request body parameters Pass the same task identifier returned by `export`: | Name | Required | Type | Description | | ----- | -------- | -------------- | ------------------------------------------------------------ | | `uid` | Yes | String (int64) | Task identifier from the `export` response, e.g. `"177458"`. | ##### Example request ```json { "uid": "177458" } ``` * 200: OK ```json { "export_messages_v2_result": { "file": "Export_Messages_v2_12345_20260813120000-a1b2c3d4.csv", "file_url": "https://app.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/download/Export_Messages_v2_12345_20260813120000-a1b2c3d4.csv" } } ``` Calling `result` before `status` reports `"STATUS_SUCCESS"` returns an empty result. `file_url` is a ready-to-use link to the [download endpoint](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#download) on the host of the account’s data center. Use it as-is instead of building the URL from `file` yourself. `file_url` is empty if no download base URL is configured for the account’s data center. In that case, a URL built from `file` works only for accounts that sign in at `app.pushwoosh.com`. ### lastTasks Lists recent export tasks for an application, most recent first. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/lastTasks` ##### Request body parameters Every parameter is an optional filter; omit them all to list every task the token has access to: | Name | Required | Type | Description | | ---------------- | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | No | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). Omit to list tasks across all applications the token has access to. | | `types` | No | Array | Restrict to specific task types. Use `[“TASK_TYPE_EXPORT_MESSAGES_V2”]` to only see message exports. | | `campaign` | No | String | Filter by [campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). | | `message_id` | No | String (uint64) | Filter by a single numeric message ID, quoted. | | `message_code` | No | String | Filter by a single [message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code). | | `limit` | No | Integer | Maximum number of tasks to return. | | `timestamp_from` | No | String | Only return tasks created after this timestamp (RFC 3339). | Note Tasks are kept for 30 days regardless of whether their file has already been deleted after the 7-day file retention window. `lastTasks` can still show a task whose `result` no longer resolves to a downloadable file. ##### Example request ```json { "application": "XXXXX-XXXXX", "types": ["TASK_TYPE_EXPORT_MESSAGES_V2"], "limit": 10 } ``` * 200: OK ```json { "tasks": [ { "id": "177458", "timestamp": "2026-08-13T12:00:00Z", "status": "STATUS_SUCCESS", "requested_by_user": "user@example.com", "export_messages_v2": { "application_code": "XXXXX-XXXXX" }, "export_messages_v2_result": { "file": "Export_Messages_v2_12345_20260813120000-a1b2c3d4.csv", "file_url": "https://app.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/download/Export_Messages_v2_12345_20260813120000-a1b2c3d4.csv" } } ] } ``` ### delete Deletes a task and its file before the 7-day retention window expires. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/delete` ##### Request body parameters Pass the task identifier returned by `export`: | Name | Required | Type | Description | | ----- | -------- | -------------- | ------------------------------------------------------------ | | `uid` | Yes | String (int64) | Task identifier from the `export` response, e.g. `"177458"`. | ##### Example request ```json { "uid": "177458" } ``` * 200: OK ```json {} ``` ### Download Downloads the CSV file generated by `result`, by name. `GET` `https://app.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/download/` ##### Headers Authenticate the same way as the other methods, or rely on an active Control Panel session: | Name | Required | Description | | --------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token), in the same format as the other `exportMessagesStatistics` methods: `Authorization: Api ` (the `Api` scheme is case-insensitive). A request with no `Authorization` header and no logged-in Control Panel session gets `401 Unauthorized`. | Replace `` with the exact `file` value from the `result` response, for example: ```plaintext https://app.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/download/Export_Messages_v2_12345_20260813120000-a1b2c3d4.csv ``` Caution Building this URL by hand works only for accounts that sign in at `app.pushwoosh.com`, because the host can’t be derived from `file` alone. Use the `file_url` from [`result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) or [`lastTasks`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#lasttasks) instead, because it already points at the host of the account’s data center. See [`result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) for what to do if `file_url` comes back empty. The file is a CSV containing the columns selected in `properties`. It stays available for 7 days after the export finishes, then the cleanup job removes it and the URL stops resolving. ### Message recipients export The same `export`, `status`, `result`, `lastTasks`, `delete`, and `download` methods also export the individual recipients of one message. Use `"type": "TASK_TYPE_EXPORT_MESSAGE_RECIPIENTS"` and an `export_message_recipients` object instead of `export_messages_v2`. `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/export` Not the Customer Journey Holdout Split This exports recipients a **message** excluded. A Customer Journey [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) point is a different mechanism. It excludes users from the rest of a *journey* before any message is sent, and its `exportJourneyPointUsers` task (same `export`/`status`/`result` methods, a different `type`) reports branch membership, not send outcomes. ##### export_message_recipients request fields | Name | Required | Type | Description | | ----------------------- | -------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_id` | See note | String (uint64) | A single numeric message ID. Required if `campaign` is not set. | | `campaign` | See note | String | A single [campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). Required if `message_id` is not set. Setting both narrows to the message’s rows within that campaign. | | `application` | Yes | String | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) the message belongs to. | | `type` | Yes | String | Which recipients to export. One of `"sends"`, `"deliveries"`, `"opens"`, `"holdout"` (see below). | | `date_from` / `date_to` | No | String (date-time) | Restrict rows to this window. Omit both for the message’s full history. | Omitting both `message_id` and `campaign`, or omitting `application`, fails with an error naming the missing field. A `message_id` from a different account fails with `NotFound: message not found`, checked before the export runs. A `message_id` or `campaign` that belongs to a different application in the same account isn’t rejected. It matches no rows, and the CSV downloads with only a header. `type` selects the recipient set: | Value | Rows returned | | -------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `"sends"` | Every recipient the message was accepted for. | | `"deliveries"` | Recipients from `sends` whose delivery the provider confirmed. | | `"opens"` | Recipients from `sends` who opened the message. | | `"holdout"` | Recipients this message excluded, not any other message. Nothing was sent to them. An account-wide [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) or the message’s own `holdout_percentage` (see [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/)) can both produce this row. | ##### Example request ```json { "type": "TASK_TYPE_EXPORT_MESSAGE_RECIPIENTS", "export_message_recipients": { "message_id": "22875465358", "application": "6D382-5ADFE", "type": "holdout", "date_from": "2026-08-01T00:00:00Z", "date_to": "2026-09-30T00:00:00Z" } } ``` * 200: OK ```json { "uid": "29820" } ``` Poll [`status`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#status) and call [`result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result) the same way as a message-history export. `result` returns `export_message_recipients_result` instead of `export_messages_v2_result`: ```json { "export_message_recipients_result": { "file": "Message_22875465358_Holdout_29820-fe5d8ba468df25b7.csv", "file_url": "https://app.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/download/Message_22875465358_Holdout_29820-fe5d8ba468df25b7.csv" } } ``` `sends`, `deliveries`, and `opens` share one column set: `hwid, user_id, platform, send_timestamp, delivery_timestamp, open_timestamp, send_error, pae, send_status`. `pae` is `false` when the platform accepted the send but showed no visible alert for it, the same case [`unshowable_sends`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/) counts on the aggregate stats. `holdout` has its own, shorter set: `hwid, user_id, platform, holdout_timestamp`. --- # Export the uplift report **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-uplift-report/ How to export the uplift report for a campaign or journey to CSV, its request fields, and every column in the resulting file --- Exports the [uplift report](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/), a campaign’s or a journey’s holdout metrics, to a CSV file, through the same task lifecycle as [message statistics export](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#how-the-export-flow-works): call `export`, poll `status`, call `result`, then download. Not the control group export `TASK_TYPE_EXPORT_CONTROL_GROUP` exports a control group’s member list: devices and user counts. `TASK_TYPE_EXPORT_CONTROL_GROUP_ANALYTICS`, documented here, exports the uplift report’s metrics instead: no member list, no device data. They’re separate task types with separate arguments. ## Request `POST` `https://api.pushwoosh.com/api/v2/statistics/exportMessagesStatistics/export` Use `"type": "TASK_TYPE_EXPORT_CONTROL_GROUP_ANALYTICS"` and an `export_control_group_analytics` object instead of `export_messages_v2`. Authentication and the rest of the lifecycle (`status`, `result`, `lastTasks`, `delete`, download) work exactly as described on [message statistics export](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/). `export_control_group_analytics` accepts: | Name | Required | Type | Description | | ------------------ | -------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `application_code` | Yes | String | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `date_from` | Yes | String | Window start over the report’s own timestamp, `YYYY-MM-DD` or `YYYY-MM-DD HH:MM:SS`. A date-only value expands to the start of that day. | | `date_to` | Yes | String | Window end, same format. A date-only value expands to the end of that day. | | `campaign` | No | String | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code), exports that campaign’s holdout only. Mutually exclusive with `journey`. | | `journey` | No | String | Journey UUID, exports that journey’s Holdout Split points only. Mutually exclusive with `campaign`. | Leave both `campaign` and `journey` empty to export every campaign and every journey the application has a report for. Important The window can’t exceed 90 days. A wider window isn’t trimmed: the request fails with `400` and the message `exceeded the maximum date interval. Max interval: 90 days`. The [uplift report itself keeps 90 days](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#how-far-back-the-report-can-look) of rows, so a wider request couldn’t return anything more anyway. ### Example request ```json { "type": "TASK_TYPE_EXPORT_CONTROL_GROUP_ANALYTICS", "export_control_group_analytics": { "application_code": "XXXXX-XXXXX", "date_from": "2026-08-01", "date_to": "2026-08-31" } } ``` `result` returns `export_control_group_analytics_result.file` and `.file_url`, the same shape as [message statistics export’s `result`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/#result). ## What’s in the file One row per campaign or per journey holdout point, per goal event. The underlying report writes a fresh row every day for the same pair, and the export keeps only the newest one, so you get the current numbers, not a history of every past run. | Column | Description | | ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `timestamp` | When this row was computed. | | `owner_type` | `TYPE_CAMPAIGN` or `TYPE_JOURNEY`. | | `owner_id` | The campaign or message code for a campaign row, the journey’s UUID for a journey row. | | `point_uuid` | The Holdout Split point’s UUID for a journey row. Empty for a campaign row. | | `event_name` | The goal event this row measures. | | `attribution_window_seconds` | The attribution window applied, in seconds. | | `treatment_users`, `control_users`, `treatment_conversions`, `control_conversions`, `treatment_events`, `control_events` | Raw counts behind the metrics below. | | `uplift_pct`, `incremental_events`, `percent_of_treatment`, `z_score`, `p_value`, `confidence_pct` | Same metrics as the [uplift report](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#metrics-in-the-report). | | `significance` | `TYPE_SIGNIFICANT`, `TYPE_NOT_SIGNIFICANT`, or `TYPE_NOT_ENOUGH_DATA`. | | `revenue_tracked` | `1` if the goal event’s revenue source was configured, `0` if the row is only guessing from a legacy fallback. See [Revenue in the report](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#revenue-in-the-report). | | `treatment_revenue_`, `control_revenue_`, `incremental_revenue_` | Three columns per currency, lowercase currency code (for example `treatment_revenue_usd`). The file only gets columns for currencies that actually appear in the exported rows. | Note When `revenue_tracked` is `0`, every revenue cell in that row is empty, not `0.00`. A blank cell means the source wasn’t configured, not that the send made no money. --- # In-app statistics **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/inapp-statistics/ Retrieve impression, interaction and frequency capping metrics for instant in-app campaigns, as totals or as a time series. --- Two methods return statistics for [instant in-app campaigns](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/). Use `inapps:totals` for period totals across one or more campaigns, and `inapps:timeline` for a time series of a single campaign. The values match the [in-app statistics screen](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) in the Control Panel. In-apps sent from a Journey These methods cover instant in-app campaigns only. In-apps delivered by a Customer Journey element are reported per journey point by [Get Journey stats](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/). ## Metrics | Field | Type | Description | | --------------------- | ------ | -------------------------------------------------------------------------------------- | | `impressions` | number | How many times the in-app was displayed. Labeled **Impressions** in the Control Panel. | | `unique_impressions` | number | Number of unique devices the in-app was displayed to. | | `interactions` | number | Interactions with the in-app content: button clicks, link clicks and form submits. | | `unique_interactions` | number | Number of unique devices that interacted with the in-app. | | `skips` | number | How many times users dismissed the in-app without interacting. | | `audience` | number | Number of unique devices that produced any in-app event in the period. | Note Unique metrics and `audience` are approximate because they use a probabilistic distinct count. Platform figures do not sum to the campaign total: a device that appears on two platforms is counted once in each platform breakdown and once in the campaign total. ## inapps:totals Returns totals for the period. Pass `inapp_codes` to report on specific campaigns, or omit it to walk every in-app of the application page by page. That is the method to use for a scheduled export. `POST` `https://api.pushwoosh.com/api/v2/statistics/inapps:totals` ##### Headers | Name | Required | Description | | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) in the format `Authorization: Api `. | ##### Request body parameters | Name | Required | Type | Description | | ---------------- | -------- | ------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | Yes | String | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `date_range` | Yes | Object | Reporting period. `date_from` and `date_to` use the `YYYY-MM-DD` format and are inclusive. The period is counted in UTC and must not exceed 366 days. | | `inapp_codes` | No | Array | [In-app codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#in-app-code), up to 100 per request. Omit to report on all in-apps of the application. If any code in the list does not belong to the application, the whole request fails with `404`. | | `platforms` | No | Array | Restrict the metrics to these platforms. Possible values: `"IOS"`, `"ANDROID"`, `"HUAWEI_ANDROID"`, `"AMAZON"`, `"OSX"`, `"WINDOWS"`, `"SAFARI"`, `"CHROME"`, `"FIREFOX"`, `"WEB"`. | | `with_platforms` | No | Boolean | Add a per-platform breakdown to every item. | | `page` | No | Integer | Page number, starting at `0`. Applies when `inapp_codes` is omitted. | | `per_page` | No | Integer | Items per page, `20` by default, `100` at most. | ##### Example request ```bash curl -X POST https://api.pushwoosh.com/api/v2/statistics/inapps:totals \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "application": "XXXXX-XXXXX", "date_range": { "date_from": "2026-07-01", "date_to": "2026-07-31" }, "inapp_codes": ["AAAAA-BBBBB"], "with_platforms": true }' ``` ##### Response fields `total` is the number of in-apps the request matches. When `inapp_codes` is omitted, that is all in-apps of the application, so pagination can walk it. `items` holds the current page. `page` counts from `0`. ##### Example response ```json { "total": 1, "page": 0, "per_page": 20, "items": [{ "inapp": { "code": "AAAAA-BBBBB", "name": "Summer sale", "status": "active", "rich_media_code": "CCCCC-DDDDD" }, "metrics": { "impressions": 15230, "unique_impressions": 9120, "interactions": 2311, "unique_interactions": 1980, "skips": 640, "audience": 9120 }, "platforms": [{ "platform": "IOS", "metrics": { "impressions": 8100, "unique_impressions": 4900, "interactions": 1300, "unique_interactions": 1120, "skips": 310, "audience": 4900 } }], "frequency_capping": { "suppressions": 45, "affected_users": 30, "data_available_from": "2026-07-17" } }] } ``` ## inapps:timeline Returns period totals and a time series for one in-app campaign. `POST` `https://api.pushwoosh.com/api/v2/statistics/inapps:timeline` ##### Request body parameters | Name | Required | Type | Description | | ---------------- | -------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | Yes | String | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `inapp_code` | Yes | String | [In-app code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#in-app-code). | | `date_range` | Yes | Object | Reporting period. `date_from` and `date_to` use the `YYYY-MM-DD` format and are inclusive. The period is counted in UTC and must not exceed 366 days. Optional `interval`: `"HOUR"`, `"DAY"` (default), `"WEEK"` or `"MONTH"`. `"HOUR"` is available for periods up to 31 days. | | `platforms` | No | Array | Restrict the metrics to these platforms. | | `with_platforms` | No | Boolean | Add a per-platform breakdown to the totals and to every row. | ##### Example request ```bash curl -X POST https://api.pushwoosh.com/api/v2/statistics/inapps:timeline \ -H "Authorization: Api YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "application": "XXXXX-XXXXX", "inapp_code": "AAAAA-BBBBB", "date_range": { "date_from": "2026-07-01", "date_to": "2026-07-07", "interval": "DAY" } }' ``` ##### Example response ```json { "inapp": { "code": "AAAAA-BBBBB", "name": "Summer sale", "status": "active", "rich_media_code": "CCCCC-DDDDD" }, "totals": { "impressions": 15230, "unique_impressions": 9120, "interactions": 2311, "unique_interactions": 1980, "skips": 640, "audience": 9120 }, "frequency_capping": { "suppressions": 45, "affected_users": 30, "data_available_from": "2026-07-17" }, "rows": [{ "timestamp": "2026-07-01T00:00:00Z", "metrics": { "impressions": 2140, "unique_impressions": 1700, "interactions": 320, "unique_interactions": 290, "skips": 95, "audience": 1700 } }], "impression_duration": [ { "from_seconds": 0, "to_seconds": 5, "count": 3200 }, { "from_seconds": 5, "to_seconds": 15, "count": 5400 }, { "from_seconds": 15, "to_seconds": 30, "count": 4100 }, { "from_seconds": 30, "to_seconds": 0, "count": 2530 } ] } ``` `impression_duration` buckets how long the in-app stayed on screen. In the last bucket `to_seconds` is `0`, which means “30 seconds and longer”. ## Frequency capping The `frequency_capping` block reports in-app displays that [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) prevented, and the number of unique users affected. Suppressions are not counted in `impressions`. Suppression data starts on 17 July 2026 Suppressions were not recorded before that date. For a period that ends earlier, the `frequency_capping` block comes back as `null` instead of zeros, so a lack of data is never mistaken for capping that suppressed nothing. `data_available_from` in the response states the first covered day. Frequency capping has no per-platform breakdown. ## Data retention Statistics are kept for 365 days, so a period that starts earlier returns no data for the uncovered days. ## Response codes | Code | Meaning | | ---- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | | 200 | Success. | | 400 | Invalid request: a missing or malformed `date_range`, a period longer than 366 days, an hourly interval over more than 31 days, or more than 100 `inapp_codes`. | | 401 | Missing or invalid API token. | | 404 | An in-app code was not found in this application. | --- # Message statistics **URL:** https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/ List of API methods for retrieving and analyzing message statistics, including delivery rates, open rates, and user engagement. --- These methods return statistics for the messages you have sent. Use them to list sent messages, get hourly metrics for a message, see where a message lost its audience in the delivery funnel, read per-device message logs, and count link clicks in emails. ## messages:list Displays the list of sent messages. `POST` `https://api.pushwoosh.com/api/v2/messages:list` ##### Headers | Name | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). Must be provided in the following format: `Authorization: Api `. | ##### Request body parameters | Name | Required | Type | Description | | ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `platforms` | No | Array | Message platforms. Possible values: `"IOS"`, `"ANDROID"`, `"OSX"`, `"WINDOWS"`, `"AMAZON"`, `"SAFARI"`, `"CHROME"`, `"FIREFOX"`, `"IE"`, `"EMAIL"`, `"HUAWEI_ANDROID"`, `"SMS"`. | | `date_range` | No | Object | Reporting period, filtered on message creation date. `date_from` and `date_to` must follow the `YYYY-MM-DD` format (e.g., `"2000-01-01"`); both days are included in full, so `date_from` and `date_to` set to the same date return that whole day. Dates are interpreted in UTC, not in your account timezone. | | `campaign` | No | String | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) | | `filters` | Yes | Object | Message filters. | | `source` | No | String | Message source. For example: `AB_TEST`, `API`, `AUTO_PUSH`, `CP`, `CSV`, `CUSTOMER_JOURNEY`, `EMAIL_API`, `EMAIL_CP`, `GEO_ZONE`, `PUSH_ON_EVENT`, `RSS`. | | `messages_codes` | No | Array | [Message codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained from `/createMessage` API responses. | | `messages_ids` | No | Array | Message IDs obtained from the Message History | | `message_type` | No | String | Message flow filter. Possible values: `"TRANSACTIONAL"`, `"BROADCAST"`, `"ALL"`. Defaults to `"ALL"` when omitted. | | `params` | No | Object | Specify whether to show message details and metrics. Set `with_details: true` to include the `"details"` object and `with_metrics: true` to include the `"metrics"` object in the response. | | `application` | Yes | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). | | `per_page` | No | Integer | Number of results per page, `1` to `499`. Omit the parameter to get the default page size of 500 results; passing `500` or more explicitly is rejected with `400`. | | `page` | No | Integer | Zero-based page number for pagination. See the deep pagination limit below. | Deep pagination `messages:list` rejects requests where `page × per_page` exceeds 100000 results. Narrow the `date_range` or use a smaller `per_page` to stay within the limit. `total` is the exact number of messages matching the filters while that number is at or below 100000, and stops at `100001` above it — more than this endpoint can page through. Do not size a page loop past that limit: pages beyond it are rejected, not empty. To pull a period that holds more messages than that, narrow the `date_range` per request, or use the [asynchronous export](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-messages-statistics-api/), which writes the whole period to one CSV file without paging. `message_type` also bounds how far back a dateless request reaches. 31-day default window for ALL and TRANSACTIONAL Omitting `date_range` with `message_type` set to `"ALL"` (including the default) or `"TRANSACTIONAL"` returns only messages created in the last 31 days, today and the preceding 30 days, UTC. `"BROADCAST"` carries no such window, and filtering by `messages_ids` or `messages_codes` skips it regardless of `message_type`, since both scope the result to specific messages already. Set `date_range` explicitly to reach further back. ##### Example request ```json { "filters": { "platforms": [], // IOS, ANDROID, OSX, WINDOWS, AMAZON, SAFARI, CHROME, FIREFOX, IE, EMAIL, HUAWEI_ANDROID, SMS "date_range": { "date_from": "string", // Required format: 2000-01-01 "date_to": "string" // Required format: 2000-01-01 }, "source": "API", // AB_TEST, API, AUTO_PUSH, CP, CSV, CUSTOMER_JOURNEY, EMAIL_API, EMAIL_CP, GEO_ZONE, PUSH_ON_EVENT, RSS "campaign": "string", // Campaign code "messages_ids": [], // Message IDs "messages_codes": [], // Message codes "message_type": "ALL", // TRANSACTIONAL, BROADCAST, ALL (default) "application": "string" // Pushwoosh application code }, "params": { "with_details": true, // Add message details to the response ("details" object) "with_metrics": true // Add message metrics to the response ("metrics" object) }, "per_page": 20, // <= 499 "page": 0 } ``` Response codes and examples * 200: OK ```json { "total": 0, "items": [{ "id": 0, "code": "string", "created_date": "string", "send_date": "string", "status": "string", "platforms": [], "source": "string", "push_info": { "details": { "title": "string", "filter_name": "string", "filter_code": "string", "content": { "key": "value" }, "platform_parameters": { "android_header": "string", "android_root_params": { "key": "value" }, "ios_title": "string", "ios_subtitle": "string", "ios_root_params": { "key": "value" }, "chrome_header": "string", "chrome_root_params": { "key": "value" }, "firefox_header": "string", "firefox_root_params": { "key": "value" }, "conditions": [ // tag conditions (see /developer/api-reference/messages-api/#tag-conditions) TAG_CONDITION1, TAG_CONDITION2, ..., TAG_CONDITIONN ], "conditions_operator": "AND", // logical operator for conditions arrays; possible values: AND, OR "data": { "key": "value" } }, "follow_user_timezone": true }, "metrics": [{ "sends": 0, "opens": 0, "deliveries": 0, "inbox_opens": 0, "unshowable_sends": 0, "errors": 0, "platform": 0 }] }, "email_info": { "details": { "template": "string", "filter_name": "string", "filter_code": "string", "subject": { "key": "value" }, "from_name": "string", "from_email": "string", "reply_name": "string", "reply_email": "string", "follow_user_timezone": true, "conditions": [ // tag conditions (see Messages-api - tag-conditions) TAG_CONDITION1, TAG_CONDITION2, ..., TAG_CONDITIONN ], "conditions_operator": "AND" // logical operator for conditions arrays; possible values: AND, OR }, "metrics": [{ "sends": 0, "opens": 0, "deliveries": 0, "hard_bounces": 0, "soft_bounces": 0, "rejects": 0, "confirmed_sends": 0, "unsubs": 0, "complaints": 0, "errors": 0 }] } }] } ``` * 400: Bad Request Error `date_range` spans more than 30 days: ```json { "error": "exceeded the maximum date interval. Max interval: 30 days" } ``` `page × per_page` exceeds the deep pagination limit: ```json { "error": "requested result window is too large, narrow the date range" } ``` * 401: Incorrect API access token ```json { "error": "account not found" } ``` * 500: Internal Server Error Note For iOS, please make sure that you have added the Notification Service Extension to your project for tracking push delivery. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-message-delivery-tracking) ## totalsByIntervals Returns metrics and conversion data based on the message code, aggregated by hour. **POST** `https://api.pushwoosh.com/api/v2/statistics/messages/totalsByIntervals` ##### Authorization Authorization is handled via the API Access Token in the request header. ##### Request body parameters | Parameter Name | Type | Description | Required | | -------------- | ------ | -------------------------------------------------------------------------------------------------------------------- | -------- | | `message_code` | string | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained from `/createMessage` API responses. | Yes | | `platforms` | [int] | [Platforms](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) | No | ##### Request example ```json { "message_code": "XXXXX-XXXXXXXXX-XXXXXXXX", // required. Unique message identifier "platforms": [1, 3, 7, 10, 11, 12] // optional. List of platform codes } ``` ##### Response fields | Name | Type | Description | | ------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **`metrics`** | **array** | **Contains an array of message metrics** | | `timestamp` | string | The time of the metric. | | `platform` | int | The platform code (e.g., iOS, Android). | | `sends` | string | The number of sent messages. | | `opens` | string | The number of opened messages. | | `deliveries` | string | The number of delivered messages. | | `inbox_opens` | string | The number of inbox opens. | | `unshowable_sends` | string | The number of messages accepted for devices with notifications turned off in the OS settings. No banner is shown, but the message still reaches the app, and it appears in [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) if the send has [Save message to Inbox](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#save-message-to-inbox) enabled. Counted as successful sends, not as errors. | | `errors` | string | The number of errors. | | **`conversion`** | **object** | **Contains conversion data** | | `sends` | string | The total number of sent messages. | | `opens` | string | The total number of opened messages. | | **`events`** | **array** | **An array of events with their statistics** | | `name` | string | The name of the event (e.g., cart add). | | `hits` | string | The number of hits. | | `conversion` | float | The conversion rate relative to opens. | | `revenue` | float | The revenue (only for events with `__amount` and `__currency` attributes). | ##### Response example ```json { "metrics": [{ "timestamp": "2024-08-03 15:00:00", // Timestamp of the metrics in "YYYY-MM-DD HH:MM:SS" format "platform": 3, // Platform code "sends": "55902", // Number of messages sent "opens": "382", // Number of messages opened "deliveries": "22931", // Number of messages delivered "inbox_opens": "0", // Number of messages opened in the inbox "unshowable_sends": "2", // Sent to devices with notifications turned off, no banner shown "errors": "0" // Number of errors encountered }], "conversion": { "sends": "55902", // Total number of messages sent "opens": "772", // Total number of messages opened "events": [{ "name": "cart_add", // Name of the event "hits": "96", // Number of hits for the event "conversion": 0.12, // Conversion rate relative to opens "revenue": 0 // Revenue generated by the event (only for events with amount/currency attributes) }] } } ``` ## Delivery funnel Use the delivery funnel to see at which stage a message or campaign lost its audience (errors, no delivery, no open) and why. Unlike [`totalsByIntervals`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#totalsbyintervals), which returns hourly totals, the funnel breaks the audience down by stage and drop-out reason, one entry per channel, with a per-platform breakdown. The stage sequence differs by channel: see `stage` under `GetDeliveryFunnelResponse fields` below for which stages each channel carries. Both methods require a [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). Not scoped to the token's application A token’s app restriction is not enforced on these two methods. They return the funnel for any `message_code` or `campaign_code` that belongs to the token’s account, regardless of which application the token is restricted to. This is by design. Both methods return the same response shape, `GetDeliveryFunnelResponse`, described once below. ### getDeliveryFunnel Returns one message’s funnel. `POST` `https://api.pushwoosh.com/api/v2/statistics/messages/getDeliveryFunnel` ##### Headers | Name | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). Must be provided in the following format: `Authorization: Api `. | ##### Request body parameters | Name | Required | Type | Description | | -------------- | -------- | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_code` | Yes | String | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained from `/createMessage` API responses. | | `platforms` | No | Array of integers | [Platform codes](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) to filter the funnel by. Omit to return every platform the message was sent to. | Note The funnel takes no time-range parameters: it is computed over the message’s entire lifetime. ##### Example request ```json { "message_code": "A444-AAABBBCC-00112233", // Message code from /createMessage "platforms": [1, 14] // Optional. iOS, Email } ``` Every field is always present Every field described below is present on every response, even at its zero value: an empty array (`"pieces": []`), an unset enum as its `_UNSPECIFIED` value, or an unset timestamp as `null`. Only the value carries meaning, not whether the field is there. `count` fields serialize as JSON strings, e.g. `"count": "9820"`, not numbers. `platform` and `code` stay plain numbers. ##### GetDeliveryFunnelResponse fields | Name | Type | Description | | -------------------------- | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `channels` | Array | One entry per channel the message was sent on. | | `window_from`, `window_to` | String or null | The period the funnel covers, based on the available data. `null` when `funnel_state` is not `FUNNEL_STATE_READY`. | | `funnel_state` | String | `FUNNEL_STATE_READY`: the funnel below is populated. `FUNNEL_STATE_NO_EVENTS`: nothing has happened for this message yet. `FUNNEL_STATE_EXPIRED`: the message was sent too long ago. Funnel data for it is no longer available. `channels` is empty in both non-`READY` cases. An unknown, foreign, or deleted `message_code` is not one of these states, see the `404` response below. | **`channels[]`** | Name | Type | Description | | ---------------------- | ------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `channel` | String | `CHANNEL_MOBILE_PUSH` (iOS, macOS, Android, Amazon, Huawei), `CHANNEL_WEB_PUSH` (Safari, Chrome, Firefox), `CHANNEL_EMAIL`, `CHANNEL_OTHER` (SMS, messengers, Wallet, Windows, Baidu, Xiaomi), `CHANNEL_APP_INBOX`. | | `funnel` | Array | Funnel stages for this channel, see `funnel[]` below. | | `deliveries_form` | String | Which breakdown the `STAGE_DELIVERIES` stage carries: `DELIVERIES_FORM_PER_DEVICE` (four disjoint rows, split by whether the device would show an alert and whether it confirmed) or `DELIVERIES_FORM_BASIC` (two rows, no alert-state split). | | `basic_form_reason` | String | Meaningful only when `deliveries_form` is `DELIVERIES_FORM_BASIC`: `BASIC_FORM_REASON_RETENTION` (the message is too old for a per-device breakdown), `BASIC_FORM_REASON_UNAVAILABLE` (a per-device breakdown isn’t available for this account or message, for example a system message), `BASIC_FORM_REASON_NO_DELIVERIES` (nothing accepted yet), `BASIC_FORM_REASON_NOT_APPLICABLE` (the channel has no alert state at all, for example email or App Inbox). Otherwise `BASIC_FORM_REASON_UNSPECIFIED`: ignore this field unless `deliveries_form` is `DELIVERIES_FORM_BASIC`. | | `confirmed_deliveries` | Object | `count` and a per-`platforms` breakdown of confirmed deliveries. Not capped at the `STAGE_DELIVERIES` count, so it can differ from it. | **`funnel[]` (one stage)** | Name | Type | Description | | ----------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `stage` | String | `STAGE_AUDIENCE` (everyone the message targeted), `STAGE_SENT` (accepted by the push service or email provider), `STAGE_ERRORS` (rejected before it was accepted), `STAGE_DELIVERIES` (accepted, split by confirmation), `STAGE_OPENED` (unique devices that opened, or for App Inbox the entries the user tapped open), `STAGE_INTERACTIONS` (email only: clicked, unsubscribed, complained), `STAGE_REACHED`/`STAGE_READ` (App Inbox only, replace `STAGE_DELIVERIES`). | | `count` | String | Total for the stage, as a numeric string. | | `pieces` | Array | The stage’s own breakdown. Empty on `STAGE_ERRORS`, which uses `errors` instead. Each piece has `kind` (`KIND_PASSED` moved on to the next stage, `KIND_REASON` did not, `KIND_SUBSET` overlaps the other two and is never summed), `category` (see the value list below each stage), `count`, and a `platforms` breakdown. | | `errors` | Array | `STAGE_ERRORS` only: `category` (see the value list below), `count`, `platforms`, and `codes`: the raw provider error codes grouped into this category (`platform`, `code`, `name`, `count`). For what each `name` means and how to fix it, see [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/). | | `platforms` | Array | The stage’s total, split by platform. | **`category` values** `STAGE_AUDIENCE`: * **`ELIGIBLE_AUDIENCE`** (passed): not excluded by any of the reasons below. * **`FREQUENCY_CAPPING`**: skipped by frequency capping. * **`CONTROL_GROUP`**: held out as a control group. * **`UNSUBSCRIBED`**: the recipient had unsubscribed. * **`BOUNCED`**: the recipient’s address had previously bounced. * **`COMPLAINT`**: the recipient had previously marked a message as spam. * **`FILTERED_BY_CATEGORY`**: the recipient had opted out of this message’s category. `STAGE_ERRORS`: * **`NO_TOKEN`**: the device had no push token or address to send to. * **`NO_DEVICE`**: no matching device was found. * **`PLATFORM_DISABLED`**: the platform is disabled for this app. * **`INVALID_TOKEN`**: the provider rejected the device token or address as invalid. * **`QUOTA_EXCEEDED`**: the provider rate-limited the send. * **`INVALID_CONTENT`**: the message couldn’t be sent as composed. Most often the template itself failed to build, for example a broken Connected Content or Liquid block, rather than the provider rejecting it. * **`INVALID_CONFIGURATION`**: the account’s own provider setup is broken, for example wrong credentials, a deleted Firebase project, or a disallowed APNs topic. * **`PROVIDER_ERROR`**: the provider returned an error outside the categories above. * **`INTERNAL_ERROR`**: Pushwoosh failed to process the send. * **`UNCLASSIFIED_ERROR`**: a drop-out that matches none of the categories above. Expected to be rare. `STAGE_DELIVERIES`, `DELIVERIES_FORM_PER_DEVICE`: * **`DISPLAYABLE_CONFIRMED`** (passed): the device showed an alert and confirmed receipt. * **`DISPLAYABLE_NO_CONFIRMATION`**: the device would show an alert but hasn’t confirmed receipt yet. * **`NOT_DISPLAYABLE_CONFIRMED`** (passed): a silent push confirmed receipt. * **`NOT_DISPLAYABLE_NO_CONFIRMATION`**: a silent push hasn’t confirmed receipt yet. `STAGE_DELIVERIES`, `DELIVERIES_FORM_BASIC`: * **`CONFIRMED_BY_DEVICE`** (passed): the device confirmed receipt. * **`NO_CONFIRMATION`**: not confirmed yet. Email also adds, as a subset overlapping `CONFIRMED_BY_DEVICE`: * **`BOUNCED_HARD`** (subset): the address is permanently undeliverable. * **`BOUNCED_SOFT`** (subset): the address is temporarily undeliverable, for example a full mailbox. `BOUNCED_HARD` may also split into these subset rows: * **`BOUNCED_HARD_GENERAL`**: permanently undeliverable, no more specific reason available. * **`BOUNCED_HARD_MAILBOX_FULL`**: the recipient’s mailbox is full. * **`BOUNCED_HARD_MAILBOX_INACTIVE`**: the recipient’s mailbox no longer exists. * **`BOUNCED_HARD_SUPPRESSED`**: the provider has the address blocklisted. * **`BOUNCED_HARD_UNDETERMINED`**: permanently undeliverable, reason not determined. `BOUNCED_SOFT` may also split into these subset rows: * **`BOUNCED_SOFT_GENERAL`**: temporarily undeliverable, no more specific reason available. * **`BOUNCED_SOFT_MAILBOX_FULL`**: the recipient’s mailbox was full at the time. * **`BOUNCED_SOFT_CONTENT_REJECTED`**: the recipient’s mail server rejected the message content. * **`BOUNCED_SOFT_SPAM`**: the recipient’s mail server flagged the message as spam. * **`BOUNCED_SOFT_MESSAGE_TOO_LARGE`**: the message was too large for the recipient’s mailbox. * **`BOUNCED_SOFT_ATTACHMENT_REJECTED`**: the recipient’s mail server rejected an attachment. * **`BOUNCED_SOFT_CUSTOM_TIMEOUT_EXCEEDED`**: the recipient’s mail server took too long to respond. * **`BOUNCED_SOFT_UNDETERMINED`**: temporarily undeliverable, reason not determined. * **`BOUNCED_UNDETERMINED`**: the recipient’s server accepted the email but never confirmed delivery; the outcome is unknown and the address isn’t suppressed. Counts as soft. `BOUNCED_UNCLASSIFIED` is a bounce, hard or soft, that doesn’t match any subtype above. Expected to be rare, and not a value to build logic on. `STAGE_OPENED`, email only (other channels report the stage’s `count` with no pieces): * **`OPENED_BY_RECIPIENT`** (passed): opened by a person. * **`MACHINE_OPENS_ONLY`**: opened only by an automated scanner. * **`OPEN_TYPE_UNKNOWN`**: couldn’t be classified as a person or a scanner. * **`MACHINE_OPENS_AMPP`** (subset): replaces the three rows above when the person/scanner split isn’t available for this message. Counts Apple Mail Privacy Protection’s own machine-open number instead, and overlaps the stage total. `STAGE_INTERACTIONS`, email only: * **`CLICKED_ONLY`** (passed): clicked a link, stayed subscribed. * **`CLICKED_AND_UNSUBSCRIBED`**: clicked a link and unsubscribed. * **`CLICKED_AND_COMPLAINED`**: clicked a link and marked the message as spam. * **`UNSUBSCRIBED_WITHOUT_CLICK`**: unsubscribed without clicking. * **`COMPLAINED_WITHOUT_CLICK`**: marked the message as spam without clicking. `STAGE_REACHED`, App Inbox only: * **`REACHED`** (passed): the entry was fetched by the app at least once. * **`NOT_FETCHED_YET`**: not fetched yet. `STAGE_READ`, App Inbox only: * **`READ`** (passed): marked as read. * **`DISMISSED_UNREAD`**: deleted by the user without being read. * **`UNREAD`**: still sitting unread in the inbox. * **`EXPIRED_UNREAD`**: same as `UNREAD`, but the entry has since expired out of the inbox. * **`DISMISSED`** (subset): every deleted entry, read or not. Overlaps `READ` and `DISMISSED_UNREAD`. Response codes * 200: OK ```json { "channels": [{ "channel": "CHANNEL_MOBILE_PUSH", "funnel": [ { "stage": "STAGE_AUDIENCE", "count": "10000", "pieces": [ { "kind": "KIND_PASSED", "category": "ELIGIBLE_AUDIENCE", "count": "10000", "platforms": [{ "platform": 1, "count": "10000" }] } ], "errors": [], "platforms": [{ "platform": 1, "count": "10000" }] }, { "stage": "STAGE_SENT", "count": "9820", "pieces": [], "errors": [], "platforms": [{ "platform": 1, "count": "9820" }] }, { "stage": "STAGE_ERRORS", "count": "180", "pieces": [], "errors": [ { "category": "INVALID_TOKEN", "count": "180", "platforms": [{ "platform": 1, "count": "180" }], "codes": [ { "platform": 1, "code": 1002, "name": "BadDeviceToken", "count": "180" } ] } ], "platforms": [{ "platform": 1, "count": "180" }] }, { "stage": "STAGE_DELIVERIES", "count": "9820", "pieces": [ { "kind": "KIND_PASSED", "category": "DISPLAYABLE_CONFIRMED", "count": "8000", "platforms": [{ "platform": 1, "count": "8000" }] }, { "kind": "KIND_REASON", "category": "DISPLAYABLE_NO_CONFIRMATION", "count": "820", "platforms": [{ "platform": 1, "count": "820" }] }, { "kind": "KIND_PASSED", "category": "NOT_DISPLAYABLE_CONFIRMED", "count": "900", "platforms": [{ "platform": 1, "count": "900" }] }, { "kind": "KIND_REASON", "category": "NOT_DISPLAYABLE_NO_CONFIRMATION", "count": "100", "platforms": [{ "platform": 1, "count": "100" }] } ], "errors": [], "platforms": [{ "platform": 1, "count": "9820" }] }, { "stage": "STAGE_OPENED", "count": "3120", "pieces": [], "errors": [], "platforms": [{ "platform": 1, "count": "3120" }] } ], "deliveries_form": "DELIVERIES_FORM_PER_DEVICE", "basic_form_reason": "BASIC_FORM_REASON_UNSPECIFIED", "confirmed_deliveries": { "count": "8900", "platforms": [{ "platform": 1, "count": "8900" }] } }], "window_from": "2026-09-01T00:00:00Z", "window_to": "2026-09-01T00:05:00Z", "funnel_state": "FUNNEL_STATE_READY" } ``` An empty `channels` array with `funnel_state: "FUNNEL_STATE_NO_EVENTS"` means nothing has happened for this message yet. This is not an error. A `message_code` sent too long ago for funnel data to be available also still returns `200`, with an empty `channels` array and `funnel_state: "FUNNEL_STATE_EXPIRED"`. * 404: Message not found An unknown `message_code`, one that belongs to a different account, or one that has since been deleted: ```json { "error": "message not found" } ``` * 401: Invalid or missing token ```json { "error": "invalid auth token" } ``` * 500: Internal Server Error ### getCampaignDeliveryFunnel Sums `getDeliveryFunnel` over a campaign’s messages. Unique counts are calculated per message: a subscriber reached by several messages in the campaign is counted once for each message. `POST` `https://api.pushwoosh.com/api/v2/statistics/messages/getCampaignDeliveryFunnel` ##### Headers | Name | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). Must be provided in the following format: `Authorization: Api `. | ##### Request body parameters | Name | Required | Type | Description | | --------------- | -------- | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `campaign_code` | Yes | String | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). | | `platforms` | No | Array of integers | [Platform codes](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#platforms) to filter the funnel by. Omit to return every platform the campaign was sent to. | ##### Example request ```json { "campaign_code": "AAAAA-XXXXX", // Campaign code "platforms": [1, 14] // Optional. iOS, Email } ``` ##### Response Same shape as [`GetDeliveryFunnelResponse`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getdeliveryfunnelresponse-fields) above. Response codes * 200: OK An empty audience for the given `campaign_code` and `platforms` still returns `200` with an empty `channels` array and `funnel_state: "FUNNEL_STATE_NO_EVENTS"`. A campaign sent too long ago for funnel data to be available also still returns `200`, with an empty `channels` array and `funnel_state: "FUNNEL_STATE_EXPIRED"`. Neither is an error. * 404: Campaign not found An unknown `campaign_code`, one that belongs to a different account, or one whose messages have all been deleted. A campaign with only some messages deleted is unaffected: the deleted ones still count toward its funnel. ```json { "error": "campaign not found" } ``` * 401: Invalid or missing token ```json { "error": "invalid auth token" } ``` * 500: Internal Server Error ## getMessageLog Displays detailed information about the messages sent. `POST` `https://api.pushwoosh.com/api/v2/statistics/getMessageLog` ##### Headers | Name | Required | Description | | --------------- | -------- | -------------------------------------------------------------------------------------------- | | `Authorization` | Required | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | ##### Request body parameters | Name | Required | Type | Description | | ------------------ | ---------------------------------------------------------------------------- | --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_id` | No | Integer | Select messages events by Message ID obtained from message history. Example: `12345678900`. | | `message_code` | No | String | Select messages events by [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) obtained from `/createMessage` API responses. Example: `"A444-AAABBBCC-00112233"`. | | `campaign_code` | No | String | Select messages events by [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) specified in your message payload. Example: `"AAAAA-XXXXX"`. | | `hwid` | No | String or Array | Select messages events by [HWID (Hardware ID)](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) or an array of HWIDs. | | `date_from` | Required if `message_id`, `message_code`, or `campaign_code` is not provided | Datetime | Start date for filtering messages. Format: `"YYYY-MM-DD HH:MM:SS"`. Example: `"2000-01-25 00:00:00"`. | | `date_to` | Required if `message_id`, `message_code`, or `campaign_code` is not provided | Datetime | End date for filtering messages. Format: `"YYYY-MM-DD HH:MM:SS"`. Example: `"2000-01-26 00:00:00"`. | | `limit` | No | Integer | Maximum number of message events returned in a single response. Maximum value: `100000`. | | `pagination_token` | No | String | Pagination token obtained from a previous `/getMessageLog` response. Use it to retrieve additional results. | | `user_id` | No | String | Select messages events by a custom [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id). See `/registerUser` for more details. | | `application_code` | Yes | String | Select messages events by [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | `actions` | No | Array | Filter results by specific message actions. Possible values: `"sent"`, `"delivered"`, `"opened"`, `"reject"`, `"create"`, `"inbox_delivered"`, `"inbox_read"`, `"inbox_opened"`, `"inbox_deleted"`. `"create"` events are excluded from the response by default. Include `"create"` in this array to see them. `"delivered"` events additionally require delivery statistics to be enabled on the account. | | `platforms` | No | Array | Array of target platforms to filter results. Possible values: `"unknown"`, `"ios"`, `"blackberry"`, `"android"`, `"windows phone"`, `"osx"`, `"windows"`, `"amazon"`, `"safari"`, `"chrome"`, `"firefox"`, `"ie"`, `"email"`, `"fb messenger"`, `"baidu android"`, `"huawei android"`, `"sms"`, `"xiaomi"`, `"web"`, `"whatsapp"`, `"line"`, `"kakao"`, `"telegram"`, `"apple wallet"`, `"google wallet"`, `"viber"`. | | `message_type` | No | String | Filter results by message flow. `"all"` (default if omitted) returns everything, `"broadcast"` returns only mass messages (`message_id != 0`), `"transactional"` returns only messages with `message_id = 0`, including those sent from Customer Journey. | Platform values are case-sensitive Pass `platforms` values in lowercase exactly as listed above. Some values contain a space, for example `"windows phone"` and `"huawei android"`. `"Android"` is rejected with `400 invalid platform name in platforms: Android`. ##### Example request ```shell curl --location --request POST 'https://api.pushwoosh.com/api/v2/statistics/getMessageLog' \ --header 'Authorization: Key API_ACCESS_TOKEN' \ --header 'Content-Type: application/json' \ --data-raw '{ "pagination_token": "PAGINATION_TOKEN_FROM_PREVIOUS_RESPONSE", // optional, token for pagination "limit": 1000, // optional, the max number of entries for a single response "application_code": "XXXXX-XXXXX", // Pushwoosh app code "message_code": "A444-AAABBBCC-00112233", // optional, message code obtained from /createMessaage request "message_id": 1234567890, // optional, message ID obtained from Pushwoosh Control Panel "campaign_code": "AAAAA-XXXXX", // optional, code of a campaign to get the log for "hwid": "aaazzzqqqqxxx", // optional, hardware ID of a specific device targeted with a message "user_id": "user_123", // optional, ID of a user targeted with the message "date_from": "2000-01-25 00:00:00", // optional, start of the stats period "date_to": "2000-02-10 23:59:59", // optional, end of the stats period "actions": ["opened", "inbox_opened"], // optional, used for results filtration. Possible values: "sent", "delivered", "opened", "reject", "create", "inbox_delivered", "inbox_read", "inbox_opened", "inbox_deleted". The response will include all the messages with the specified action(s). "platforms": ["ios", "chrome"], // optional, used for results filtration, lowercase only. Possible values: "unknown", "ios", "blackberry", "android", "windows phone", "osx", "windows", "amazon", "safari", "chrome", "firefox", "ie", "email", "fb messenger", "baidu android", "huawei android", "sms", "xiaomi", "web", "whatsapp", "line", "kakao", "telegram", "apple wallet", "google wallet", "viber" "message_type": "broadcast" // optional, "all" (default), "broadcast" (message_id != 0), or "transactional" (message_id = 0, includes Customer Journey sends) }' ``` Important One of the following fields is required:\ - *message_id*\ *- message_code*\ *- campaign_code*\ *- hwid*\ *- user_id*\ *- date_from* and *date_to* Consider applying various filtration params to get the most of your messages stats. Response codes and examples * 200: OK ```json { "pagination_token": "PAGINATION_TOKEN_FOR_NEXT_REQUEST", "data": [{ "timestamp": "2000-01-25T11:18:47Z", "application_code": "XXXXX-XXXXX", "message_id": 12345678900, "message_code": "A444-AAABBBCC-00112233", "campaign_code": "AAAAA-XXXXX", "hwid": "aaazzzqqqqxxx", "user_id": "user_123", "platform": "android", "action": "sent", "status": "success", "push_alerts_enabled": "true" }, { "timestamp": "2000-01-25T11:18:49Z", "application_code": "XXXXX-XXXXX", "message_id": 12345678900, "message_code": "A444-AAABBBCC-00112233", "campaign_code": "AAAAA-XXXXX", "hwid": "aaazzzqqqqxxx", "user_id": "user_123", "platform": "android", "action": "delivered", "push_alerts_enabled": "true" }, { "timestamp": "2000-01-25T11:19:23Z", "application_code": "XXXXX-XXXXX", "message_id": 12345678900, "message_code": "A444-AAABBBCC-00112233", "campaign_code": "AAAAA-XXXXX", "hwid": "aaazzzqqqqxxx", "user_id": "user_123", "platform": "android", "action": "opened", "push_alerts_enabled": "true" }, { "timestamp": "2000-01-25T11:19:30Z", "application_code": "XXXXX-XXXXX", "message_id": 12345678900, "message_code": "A444-AAABBBCC-00112233", "campaign_code": "AAAAA-XXXXX", "hwid": "aaazzzqqqqxxx", "user_id": "user_123", "platform": "android", "action": "reject", "status": "failed", "error_reason": "invalid device token", "payload": "SOME_PAYLOAD_STRING" }] } ``` * 400: Bad Request ```json { "error": "exceeded the maximum date interval. Max interval: 30 days" } ``` * 401: Unauthorized ```json { "error": "account not found" } ``` * 500: Internal Server Error Each entry in `data` also carries `error_reason` (string, populated with the failure reason when `status` is `"failed"`) and an optional `payload` (string). Note Data can be downloaded up to a maximum of 30 days from the current time. A response page can come back shorter than the requested `limit`. That alone doesn’t mean the export is finished. Pagination is not done until pagination_token is empty Keep requesting with the returned `pagination_token` until the response’s `pagination_token` is empty. A short page is not a stop signal. Tip For iOS, please make sure that you have added the Notification Service Extension to your project for tracking push delivery. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-message-delivery-tracking) ## Email statistics ### linksInteractions Displays statistics on link clicks in emails `POST` `https://api.pushwoosh.com/api/v2/statistics/emails/linksInteractions` ##### Headers | Name | Required | Description | | --------------- | -------- | -------------------------------------------------------------------------------------------- | | `Authorization` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from Pushwoosh Control Panel. | ##### Request body parameters | Name | Required | Type | Description | | -------------------- | ----------------------------------------------------- | ------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `date_range` | No | Object | Defines the reporting period. Contains `date_from` and `date_to`. | | `filters` | Yes | Object | Email filters. | | `application` | Yes | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) (alternatively, specify `campaign`, `messages_ids`, or `message_codes`). | | `messages_codes` | Yes | Array | [Message codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) (alternatively, specify `application`, `campaign`, or `messages_ids`). | | `campaign` | Yes | String | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) (alternatively, specify `application`, `messages_ids`, or `message_codes`). | | `messages_ids` | Yes | Array | Message IDs (alternatively, specify `application`, `campaign`, or `message_codes`). | | `link_template` | Required if `application` or `campaign` is specified. | String | Filters email link interactions by keyword. Only links that include the specified text in their URL will be returned in the API response. For example, if your email contains links like `https://example.com/news` and `https://example.com/shop`, setting “link_template”: “shop” will return interactions for `https://example.com/shop` only. | | `email_content_code` | No | String | [Unique identifier for the email content](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#email-content-code). | | `params` | No | Object | Defines additional response options. Includes `with_full_links`, which adds a list of full links with statistics. | ##### Request example ```shell curl --location --request POST 'https://api.pushwoosh.com/api/v2/statistics/emails/linksInteractions' \ --header 'Authorization: Api API_ACCESS_TOKEN' \ --header 'Content-Type: application/json' \ --data-raw '{ "filters": { "date_range": { "date_from": "string", // Required format: 2000-01-01 "date_to": "string" // Required format: 2000-01-01 }, "campaign": "string", // Campaign code (you can specify application, messages_ids, or message_codes instead) "application": "string", // Application code (you can specify campaign, messages_ids, or message_codes instead) "messages_ids": [], // Message IDs (you can specify application, campaign, or message_codes instead) "messages_codes": [], // Message codes (you can specify application, campaign, or message_ids instead) "link_template": "string", // Link template (required if application or campaign is specified) "email_content_code": "string" // Unique identifier for the email content. }, "params": { "with_full_links": true // Specify whether to show detailed statistics. A list of full links with statistics will be passed in the full_links array. } }' ``` ##### Response codes and examples * 200: OK ```json { "items": [{ "template": "string", "link": "string", "title": "string", "clicks": 0, "full_links": [{ "full_link": "string", "clicks": 0 }] }] } ``` * 400: Bad Request ```json { "error": "exceeded the maximum date interval. Max interval: 30 days" } ``` * 401: Unauthorized ```json { "error": "account not found" } ``` * 500: Internal Server Error ### linksInteractionsDevices Shows users who clicked on links in emails `POST` `https://api.pushwoosh.com/api/v2/statistics/emails/linksInteractionsDevices` ##### Headers | Name | Required | Description | | --------------- | -------- | ------------------------------------------------------------------------------------------------ | | `Authorization` | Yes | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) from the Pushwoosh Control Panel. | ##### Request body parameters | Name | Required | Type | Description | | -------------------- | ----------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `date_range` | No | Object | Defines the reporting period. Contains `date_from` and `date_to`. | | `filters` | Yes | Object | Email filters. | | `application` | Yes | String | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) (alternatively, specify `campaign`, `messages_ids`, or `message_codes`). | | `messages_codes` | Yes | Array | [Message codes](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) (alternatively, specify `application`, `campaign`, or `messages_ids`). | | `campaign` | Yes | String | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) (alternatively, specify `application`, `messages_ids`, or `message_codes`). | | `messages_ids` | Yes | Array | Message IDs (alternatively, specify `application`, `campaign`, or `message_codes`). | | `link_template` | Required if `application` or `campaign` is specified. | String | Filters email link interactions by keyword. Only links that include the specified text in their URL will be returned in the API response. For example, if your email contains links like `https://example.com/news` and `https://example.com/shop`, setting “link_template”: “shop” will return interactions for `https://example.com/shop` only. | | `email_content_code` | No | String | [Unique identifier for the email content](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#email-content-code). | | `page` | No | Integer | Page number for pagination. | | `per_page` | No | Integer | Number of results per page (≤ 1000). | ##### Request example ```shell curl --location --request POST 'https://api.pushwoosh.com/api/v2/statistics/emails/linksInteractionsDevices' \ --header 'Authorization: Api API_ACCESS_TOKEN' \ --header 'Content-Type: application/json' \ --data-raw '{ "filters": { "date_range": { "date_from": "string", // Required format: 2000-01-01 "date_to": "string" // Required format: 2000-01-01 }, "campaign": "string", // Campaign code (you can specify application, messages_ids, or message_codes instead) "application": "string", // Application code (you can specify campaign, messages_ids, or message_codes instead) "messages_ids": [], // Message IDs (you can specify application, campaign, or message_codes instead) "messages_codes": [], // Message codes (you can specify application, campaign, or message_ids instead) "link_template": "string", // Link template (required if application or campaign is specified) "email_content_code": "string" // Unique identifier for the email content. }, "per_page": 100, "page": 0 }' ``` ##### Response codes and examples * 200: OK ```json { "total": 0, "items": [{ "timestamp": "string", "link": "string", "hwid": "string" }] } ``` * 400: Bad Request ```json { "error": "exceeded the maximum date interval. Max interval: 30 days" } ``` * 401: Unauthorized ```json { "error": "account not found" } ``` * 500: Internal Server Error ### bouncedEmails **POST** `https://api.pushwoosh.com/api/v2/statistics/emails/bouncedEmails` Provides data on email complaints, soft bounces, and hard bounces, including the date, email address, and reason for each bounce. ##### Authorization Authorization is handled via the API Access Token in the request header. ##### Request body parameters | Parameter Name | Type | Description | Required | | -------------- | ------ | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------- | | `application` | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | Yes | | `message_code` | string | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code). | Required if `date range` or `campaign` is not provided | | `campaign` | string | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code). | Required if `message_code` or `date range` is not provided | | `date_from` | string | The start date for the data in the format `YYYY-MM-DDTHH:MM:SS.000Z` (ISO 8601 standard). | Required if `message_code` or `campaign` is not provided | | `date_to` | string | The end date for the data in the format `YYYY-MM-DDTHH:MM:SS.000Z` (ISO 8601 standard). | Required if `message_code` or `campaign` is not provided | | `per_page` | int | The number of rows per page, maximum 5000. | Yes | | `page` | int | The page number, starting from zero. | Yes | | `type` | string | The type of bounce: Complaint, Softbounce, Hardbounce. | No | ##### Request example ```json { "application": "XXXXX-XXXXX", // required. Pushwoosh app code "message_code": "XXXXX-XXXXXXXXX-XXXXXXXX", // required if campaign or date range is not provided. // Unique message identifier "campaign": "XXXXX-XXXXX", // required if message_code or date range is not provided. // Campaign code "date_from": "2024-07-20T00:00:00.000Z", // required if message_code or campaign is not provided. // Start date in ISO 8601 format "YYYY-MM-DDTHH:MM:SS.SSSZ" "date_to": "2024-07-20T00:00:00.000Z", // required if message_code or campaign is not provided. // End date in ISO 8601 format "YYYY-MM-DDTHH:MM:SS.SSSZ" "per_page": 1000, // required. Number of results per page, maximum 5000 "page": 5, // optional. Page number, starting from zero "type": "Softbounce" // optional. The type of bounce: Complaint, Softbounce, Hardbounce } ``` ##### Response fields | Field Name | Type | Description | | ---------------- | ------ | ------------------------------------------------------------ | | `total` | int | The total count of rows. | | `bounced_emails` | array | An array of bounced email details. | | ├── `email` | string | The email address that bounced. | | ├── `date` | string | The date of the bounce (format: `YYYY-MM-DDTHH:MM:SS.000Z`). | | ├── `reason` | string | The reason for the bounce. | | └── `type` | string | The type of bounce: Complaint, Softbounce, Hardbounce. | ##### Response example ```json { "total": 25, // Total count of rows. "bounced_emails": [{ "email": "example@example.com", // Email address that bounced "date": "2024-07-20T00:00:00.000Z", // Bounce date in ISO 8601 format "reason": "Invalid recipient address", // Reason for the bounce "type": "Hardbounce" // Type of bounce: Complaint, Softbounce, Hardbounce }] } ``` --- # Tags API **URL:** https://docs.pushwoosh.com/developer/api-reference/tags/ Create, delete, and list tags using API methods to manage custom attributes for users and devices. --- Note To set tag values for HWIDs and UserIDs, use the [/setTags](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) method from Device API. `addTag` is optional, because `setTags` creates any tag name it doesn’t recognize yet and infers the type from the value: a whole number becomes Integer, a number with a decimal point becomes Price, a string becomes String (or Date, for a recognized date-time format such as `2024-10-02 22:11`), an array becomes List, and a boolean becomes Boolean. An auto-created tag is always User-specific (`user_specific: true`). Use `addTag` for a Version tag, since auto-creation can’t infer it, or for a General tag (`user_specific: false`). Tag auto-creation is enabled by default for the vast majority of accounts. If `setTags` doesn’t create a new tag as expected, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to check whether it’s been turned off for your account. To store structured data such as an order or a list of orders in one tag, use [object tags](https://docs.pushwoosh.com/developer/api-reference/object-tags/) (beta). `addTag` can’t create them, because it doesn’t take fields. ## addTag `POST` `https://api.pushwoosh.com/json/1.3/addTag` Creates a tag in your account. #### Request body | Name | Type | Description | | ------------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | tag* | object | Tag parameters. | | tag.name* | string | Tag name. | | tag.type* | integer | Tag type. See possible values below. | | tag.user_specific | boolean | When `true`, the tag value is stored at the user level and shared across all of a user’s devices when set by UserID. When `false` (default), the tag is device-level and set per HWID. | | tag.application_specific | boolean | Deprecated. Accepted but ignored, because tags are no longer split into application-specific and account-wide. You can omit it. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "result": true } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "tag": { "name": "TAG_NAME", // required. "type": 1, // required. See possible values below "user_specific": false // optional. true = user-level tag (shared across all devices); false = device-level tag (default) } } } ``` **Possible tag value types:** * 1 - Integer * 2 - String * 3 - List * 4 - Date * 5 - Boolean * 6 - Decimal. Ex: 19.95 * 7 - Version. Ex: “1.0.0.0” ## deleteTag `POST` `https://api.pushwoosh.com/json/1.3/deleteTag` Completely removes a tag with all the associated information from the database. #### Request body | Name | Type | Description | | ---------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | tag* | object | Tag parameters. | | tag.name* | string | Name of a [tag](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag) to delete. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "result": true } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "tag": { "name": "TAG_NAME" // required. Name of a tag to delete } } } ``` ## listTags `POST` `https://api.pushwoosh.com/json/1.3/listTags` Retrieves a list of tags on the account. #### Request body | Name | Type | Description | | ------ | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "tags": [{ "name":"Language", "type":2, "isUserSpecific":true, "isApplicationSpecific":true }, { "name":"List tag", "type":3, "isUserSpecific":false, "isApplicationSpecific":true }] } } ``` `isUserSpecific` shows whether the tag is User-specific. `isApplicationSpecific` is always `true` and is kept only for backward compatibility, because tags are no longer split into application-specific and account-wide. Don’t rely on this field. Example ```json { "request":{ "auth": "yxoPUlwqm…………pIyEX4H" // API access token from Pushwoosh Control Panel } } ``` **Tag types**: * 1 - Integer * 2 - String * 3 - List * 4 - Date * 5 - Boolean * 6 - Decimal. Ex: 19.95 * 7 - Version. Ex: “1.0.0.0” --- # Test devices API **URL:** https://docs.pushwoosh.com/developer/api-reference/test-devices/ Register and manage test devices using API methods for testing push notifications and app functionality. --- The Test devices API registers test devices in a Pushwoosh application and lists the test devices it already has. To register a test device, pass its push token or Hardware ID (HWID), its device type, and a name. Use test devices to check a message on real devices before you send it to your audience. ## createTestDevice Registers a test device for the application. `POST` `https://api.pushwoosh.com/json/1.3/createTestDevice` #### Request Body | Name | Type | Description | | -------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------ | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | push_token | string | [Push token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#push-token) of the device. Required if `hwid` is not provided. | | hwid | string | [Hardware ID (HWID)](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) of the device. Required if `push_token` is not provided. | | device_type* | integer | [Device type](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type). See possible values in `/registerDevice` method. | | name* | string | Test device name. | | description | string | Test device description. | * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Example ```json { "request": { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "push_token": "DEVICE_PUSH_TOKEN", // required if hwid is not provided. "device_type": 1, // required. Refer to /registerDevice for device types "name": "TEST_DEVICE_1", // required. Test device name "description": "android test device" // optional. Test device description } } ``` ## listTestDevices Returns all the test devices for the specific application. `POST` `https://api.pushwoosh.com/json/1.3/listTestDevices` #### Request Body | Name | Type | Description | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------ | | auth* | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "TestDevices": [{ "name": "iosDevice", "type": "1", "pushtoken": "token", "description": "ios device" }] } } ``` Example ```json { "request": { "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel "application": "XXXXX-XXXXX" // required. Pushwoosh application code } } ``` For device types see [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice). --- # User centric API **URL:** https://docs.pushwoosh.com/developer/api-reference/user-centric-api/ Register users, link user IDs with devices, remove users, and track user events with User-centric API methods. --- The User centric API works with users instead of single devices. A user is identified by a [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) that you assign, and one user can have several devices. Use these methods to link a User ID to a device, delete a user together with all their devices, and post events performed by a user. ## registerUser `POST` `https://api.pushwoosh.com/json/1.3/registerUser` Associates external User ID with a specified device. #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Required | Type | Description | | ------------ | -------- | ------- | --------------------------------------------------------------------------------------------------------------- | | userId | No | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to associate with a device. If empty, use hwid. | | application | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | hwid | Yes | string | [Pushwoosh HWID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) | | tz_offset | No | integer | Timezone offset in seconds for the device. | | device_type | No | integer | [Device type](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type). See possible values in `/registerDevice`. | Can be used later in [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) API call (the *users* parameter).\ You can call this function before you have the push token and before the [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) call. #### Request example ```json { "request": { "userId": "user_3078a", // optional. If empty, use hwid "application": "XXXXX-XXXXX", // required. Pushwoosh application code "hwid": "8f65b16df378e7a6bece9614e1530fb2", // required. Hardware device ID "tz_offset": -3600, // optional. Timezone offset in seconds "device_type": 3 // optional. Device type, see /registerDevice // for device types } } ``` #### Response example * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` Note For emails, call [`/registerEmailUser`](https://docs.pushwoosh.com/developer/api-reference/email-api/#registeremailuser). ## deleteUser `POST` `https://api.pushwoosh.com/api/v2/device-api/deleteUser` Deletes a user and all devices associated with the specified [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) within the application. The request is processed asynchronously, and the endpoint returns `200 OK` as soon as the delete request has been accepted for processing. #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | | Content-Type | Yes | application/json | | #### Request body | Name | Required | Type | Description | | ----------- | -------- | ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | | application | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | user_id | Yes | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) to delete. All devices associated with this user in the specified application will be removed. | #### Request example ```json { "application": "XXXXX-XXXXX", // required. Pushwoosh application code "user_id": "user_3078a" // required. User ID to delete } ``` #### Response example * 200 ```json { "status_code": 200, "status_message": "OK", "response": null } ``` ##### Status codes | HTTP Status code | status_code | Description | | ---------------- | ------------ | -------------------------------------------------- | | 200 | 200 | Delete request accepted | | 200 | 210 | Argument error. See status_message for more info. | | 400 | N/A | Malformed request string | | 401 | N/A | Missing or invalid Authorization token | | 500 | 500 | Internal error | ## postEvent `POST` `https://api.pushwoosh.com/json/1.3/postEvent` Triggers an event. #### Request headers | Name | Required | Value | Description | | ------------- | -------- | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | Authorization | Yes | Token `XXXX` | [API Device Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to access Device API. Replace `XXXX` with your actual Device API token. | #### Request body | Name | Required | Type | Description | | ---------------- | -------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | hwid | Yes | string | [Hardware device ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) used in a /registerDevice request. Can be used along with ‘userId’ to identify a user who has triggered an event. | | application | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | event | Yes | string | [Event name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#event-name) exactly as created in Pushwoosh Control Panel. | | attributes | Yes | object | JSON object with the event attributes. Leave it empty if no attributes need to be passed. | | timestampUTC | No | integer | Timestamp in UTC. | | timestampCurrent | No | integer | Timestamp in local time. | | userId | Yes | string | [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) associated with a user who has triggered the event. Can be replaced by or used along with an HWID. | | device_type | No | integer | [See possible values](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#device-type) | The event’s name in the request must match the event name in Pushwoosh Control Panel. Note that “attributes” property may be empty (but not omitted) in case the event has no attributes. #### Request example ```json { "request":{ "hwid": "8f65b16df378e7a6bece9614e1530fb2", // required. Hardware device ID used in a /registerDevice API. // Can be used along with an userId to identify a // user who has triggered an event. "application": "XXXXX-XXXXX", // required. Pushwoosh application code "event": "activityCompleted", // required. Event name exactly as created in Pushwoosh Control Panel "attributes": { // required. Leave empty if no attributes need to be passed. "login": "facebook", "success": "yes", "internet": "wifi" }, "timestampUTC": 1435228403, // optional. Timestamp in UTC "timestampCurrent": 1435253603, // optional. Timestamp in local time "userId": "someuser@user.com", // required. A user id which is used for identification of // users on multiple devices. Can be replaced by // or used along with an HWID. "device_type": 1 // optional. } } ``` #### Response example * 200 ```json { "status_code": 200, "status_message": "OK", "response": { "code": "61BC9-84DD0" } } ``` #### Sending conversion events To report revenue directly instead of [tracking revenue for an existing event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/), send `postEvent` with `PW_Conversion` as the event name and the transaction’s `value` and `currency` in `attributes`: ```json { "request":{ "hwid": "8f65b16df378e7a6bece9614e1530fb2", "application": "XXXXX-XXXXX", "event": "PW_Conversion", "attributes": { "value": 49.99, // required. Transaction amount "currency": "USD", // required. ISO 4217 currency code "transaction_id": "txn_8f21", // optional. Your own transaction reference "product_id": "prod_premium" // optional }, "userId": "someuser@user.com" } } ``` Note `transaction_id` is not used by Pushwoosh to deduplicate revenue — it’s stored as-is for your own reporting and reconciliation. If you report the same transaction more than once, each call is recorded separately. See [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) for how canonical events and event mapping fit together. #### Automatic revenue mapping If an event carries attributes named `__amount` and `__currency`, Pushwoosh maps it to `PW_Conversion` automatically the first time such an event arrives, and turns on revenue tracking for it, with no manual [revenue setup](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/#choose-conversion-events-and-track-their-revenue) needed. This applies to any event name other than `PW_Conversion` itself. --- # WhatsApp API **URL:** https://docs.pushwoosh.com/developer/api-reference/whatsapp/ Send personalized and automated WhatsApp messages to your users, enhancing engagement and communication. --- /createWhatsAppMessage is deprecated New integrations should use [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) — pass `platforms: ["WHATS_APP"]` to `Notify` and use the `whatsapp` block inside `payload.content.localized_content`. See the [migration guide](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/migration-from-v1/#from-createwhatsappmessage). Note Before sending WhatsApp messages, ensure that the WhatsApp platform is properly configured. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) ## createWhatsAppMessage Deprecated Used to send WhatsApp messages to users `POST` `https://api.pushwoosh.com/json/1.3/createWhatsAppMessage` ### Request body | Name | Required | Type | Description | | :-------------- | :------- | :----- | :----------------------------------------------------------------------------------------------------------- | | auth* | Yes | string | [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from Pushwoosh Control Panel. | | application* | Yes | string | [Pushwoosh application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) | | notifications* | Yes | array | Content settings. JSON array of message parameters. See details below. | ### Notification parameters | Name | Required | Type | Description | | :---------------------- | :---------------------------------------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | send_date* | Yes | string | Date and time to send the notification. Use format `YYYY-MM-DD HH:mm` or `'now'` to send immediately. | | content | Required if `content_id` is not provided. | string | Text content of the WhatsApp message. | | content_id | Required if `content` is not provided. | string | Identifier for a pre-approved WhatsApp template from your Meta account. | | devices* | Yes | array | Customer phone number (must be associated with a [User ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) using [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) and specified in the `hwid` parameter, or use `use_auto_registration`). Only one number can be specified here. | | use_auto_registration | No | boolean | If set to `true`, the phone number specified in the `devices` parameter will be automatically registered. | | content_variables | No | object | Content variables to customize the message content. Each placeholder will be replaced with corresponding dynamic values. | | button_url_variables | No | object | Dynamic URL variables for buttons. Each key represents a button index, and its value is the dynamic variable to replace in the button’s URL. **Note**: Button indexing starts at 0, with the first button as 0, the second as 1, and so on. | | header_variables | No | object | Variables for the header in a WhatsApp template message. Specify the `type` (e.g., `text`, `image`, `video`, `document`) and the corresponding value. **Example**: `"header_variables": {"image": "https://image-url.png"}` | | preset | No | string | WhatsApp Preset Code from your Control Panel. | | language | No | string | Language locale of the WhatsApp template (must match the locale in Meta WhatsApp template editor). Default: `"en_US"`. Example: `"en_GB"`. | Important *** Currently, each WhatsApp message must be sent in a separate request for each customer. ### Request example ```json { "request": { "application": "12XXX-67XXX", // required. Pushwoosh application code. "auth": "yxoPUlwqm…………pIyEX4H", // required. API access token from Pushwoosh Control Panel. "notifications": [{ "send_date": "now", // required. YYYY-MM-DD HH:mm OR "now". "content": "Hello! {{1}}", // required if content_id is not provided. Message text. "content_id": "hello_world", // required if content is not provided. WhatsApp template identifier. "devices": ["whatsapp:+1234567890"], // required. Customer WhatsApp phone number (must be associated // with a UserId using /registerDevice and specified in // the "hwid" parameter or use "use_auto_registration"). // Only one WhatsApp number can be specified here. "preset": "XXXXX-XXXXX", // optional. WhatsApp Preset Code from your Control Panel. "content_variables": { // optional. Content variables to customize the message content. "1": "John" }, "header_variables": { // optional. Variables for the WhatsApp message header. "image": "https://image-url.png" }, "language": "en_GB", // optional. Language locale for the WhatsApp template (must match the locale in Meta WhatsApp template editor). Default: "en_US". "use_auto_registration": true // optional. Automatically register WhatsApp number specified // in "devices" parameter. }] } } ``` ### Example: Sending a two-factor authentication code via WhatsApp ```json { "request": { "application":"APP_CODE", "auth":"AUTH_TOKEN", "notifications":[{ "send_date":"now", "content_id":"replace_with_your_meta_two_factor_template_name", "content_variables":{"1":"AUTH_CODE"}, "button_url_variables":{"0":"AUTH_CODE"}, "devices":["whatsapp:REPLACE_WITH_YOUR_PHONE_NO"] }] } } ``` ### Response example ```json { "status_code": 200, "status_message": "OK", "response": { "Messages": [ "9648-0B10EXXX-0D9F2XXX" ] } } ``` ### Error response ```json { "status_code": 210, "status_message": "Invalid devices list. \"devices\" must be an array.", "response": { "Messages": [] } } ``` --- # Developer changelog **URL:** https://docs.pushwoosh.com/developer/changelog/ Track monthly SDK releases, API updates, and platform changes to plan upgrades and keep your Pushwoosh integrations current. --- This page lists SDK releases, API updates, and platform changes that affect Pushwoosh integrations. ## November 2026 | Date | Component | Change | Details | | ------ | -------------------------------------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Nov 02 | Web Push SDK [v3.104.1](https://github.com/Pushwoosh/web-push-notifications/releases/tag/v3.104.1) | Breaking | `PW_SiteOpened` sends `path` in the page’s letter case and page rules match it case-sensitively. Rules and `path` conditions saved in lowercase no longer match pages with capital letters. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#enter-a-page-path) | ## October 2026 | Date | Component | Change | Details | | ------ | -------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Oct 30 | Android SDK [v6.17.2](https://github.com/Pushwoosh/pushwoosh-android-sdk/releases/tag/6.17.2) | Breaking | Rich Media no longer substitutes single-brace placeholders like `{Name\|text\|}` inside ` ``` ## Best practices for custom elements in Rich media ### Manage CSS styles carefully Ensure your CSS styles are specific to avoid conflicts with Rich Media Editor styles. Using low-specificity styles, such as those applied to tag names or common class names, may affect the appearance of the Rich Media Editor. It’s best to keep your styles local—wrap your content in a container with a unique ID or class and use this identifier in your CSS selectors for child elements. ### Include external libraries You can integrate CSS or JavaScript libraries (such as Font Awesome, Bootstrap, Tailwind CSS, etc.) to utilize their methods, icons, fonts, animations, and more. --- # Trigger customer journeys using API-based entry **URL:** https://docs.pushwoosh.com/developer/guides/customer-journey/api-based-entry/ Launch customer journeys via API requests. Automate notifications for events like restocks, price drops, or new content releases --- API-based Entry allows you to launch a customer journey the moment a specific business event occurs. To start a campaign, you have to send a special API request. ## Setting up 1. Create a journey with an API-based Entry [](https://docs.pushwoosh.com/customer-journey-api-based-entry-1.webm) 2. Double-click on the API-based Entry step. The Entry configuration window will open. 3. You can modify the push and email content every time the journey is launched by using content placeholders. The value of each placeholder can be changed in the request. If you don’t need this option, you can skip this step. > For example, you are creating a journey to notify subscribers when a new podcast episode is released. Using a content placeholder, you can change the podcast title every time you launch the journey. First, add placeholder names in the API-based Entry setup window. You can use any names that are convenient for you. [](https://docs.pushwoosh.com/customer-journey-api-based-entry-2.webm) Now, create a push or email [Preset](https://docs.pushwoosh.com/product/content/presets/) and insert the placeholder instead of the text you want to modify. The placeholder must be in one of the following formats depending on your needs: * `{placeholder_name|format_modifier|}` – if the placeholder value is not specified when launching the campaign, users will see empty space in its place. * `{placeholder_name|format_modifier}` – if the placeholder value is not specified and has not been already assigned to a user (in case you used a Tag as a placeholder), the message will not be sent. Format modifiers * CapitalizeFirst – capitalizes the first letter in a placeholder value; * CapitalizeAllFirst – capitalizes the first letters in all words in a placeholder value if the value consists of more than one word; * UPPERCASE – switches all the letters to uppercase; * lowercase – switches all the letters to lowercase; * regular – inserts a placeholder value exactly as specified in the request, with no modifications. ![Push preset editor showing placeholder syntax example with format modifiers in message content](https://docs.pushwoosh.com/customer-journey-api-based-entry-3.webp) Note You can also use an existing Tag name instead of a placeholder name. In this case, you have to configure overwriting this Tag value by the value specified in the request as described below. When configuring the Push or Email step in your journey, select the created preset and turn on the **Personalize message with event attributes** option. Select the placeholders you want to modify in the request when launching the journey. Choose an **API-based Entry** as the source and the placeholder name as the dynamic attribute: [](https://docs.pushwoosh.com/customer-journey-api-based-entry-4.webm "Push or Email step configuration showing Personalize message with event attributes option and API-based Entry source selection") Click **Apply** to save changes. 4. In the Entry configuration window, copy the request template to modify it: ![API-based Entry configuration window displaying the API request template with authorization header format](https://docs.pushwoosh.com/customer-journey-api-based-entry-5.webp) Note To launch a journey via API, you must include a valid authorization token in the Authorization header. **Required header format** ```http Authorization: Api ``` **Example** ```http Authorization: Api c8dc6435-xxxxxxxxxxxxxxx ``` 5. Add audience filters to the “filter” parameter using [Segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/). Please note that you need to set up the necessary [Tags](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/) in advance. For example, if you want to target the journey at users who added the *Socks* item to their *Wishlist*, the “filter” value must look as follows: ```plaintext "filter": "A("12345-12345") * "T("Wishlist", EQ, "Socks")" ``` In this example, you must have a *Wishlist* Tag configured in your app. Note Your application code will be automatically added to the “filter” parameter in the `A(\"12345-12345\")` format. Please do not remove or modify it. Also, please keep in mind that quotes ("") and backslashes (\) should be escaped with a backslash (\) in JSON queries. 6. If you’ve set up placeholders, specify the desired content as their values: ![API request template showing placeholder values configuration for dynamic content in journey launch](https://docs.pushwoosh.com/customer-journey-api-based-entry-6.webp) 7. If the **Message Rate Limits** option is enabled, the number of users entering the journey at once every second will be limited. You can use the default value of 5000 users per second or set another number. ![API-based Entry configuration showing Message Rate Limits option with default value of 5000 users per second](https://docs.pushwoosh.com/customer-journey-api-based-entry-7.webp) Tip We recommend keeping the value between 5000 and 10000 users per second. If the value is too low, it might take longer for your audience to enter the journey. If the value is too high, the service that processes your data can be overloaded. 8. If you plan to restart your campaign frequently and don’t want the same users to enter the journey multiple times, set [Frequency Capping](https://docs.pushwoosh.com/product/customer-journey/journey-settings#frequency-capping). > For example, you’ve created a campaign to notify users about a price reduction for a specific product. You want to re-launch the journey a few times by sending several requests with different audience filters. In this case, you can add Frequency Capping so that the notification is not sent repeatedly to users that match multiple filters. 9. If you want a journey to launch whenever a certain business event happens, automate the request using the webhook. Once the event occurs, the webhook should automatically send the request to start the journey. You can also send the request manually if you don’t need automation. Note * If you change the segmentation conditions when sending a new request, this will not affect users who have already entered the journey. * If you change the message content when sending a new request, all users will receive the new version of the message (including those who already entered the journey but have not received this message yet). --- # Extend waiting time in Wait for Trigger and Time Delay **URL:** https://docs.pushwoosh.com/developer/guides/customer-journey/presence-extension-wait-delay/ Extend user presence in wait for trigger and time delay elements if you need to keep users waiting for a longer period of time --- ## Overview The [**Wait for Trigger**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) and [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) elements control how long users stay in a paused state within a Journey. Using the `extend_wait` parameter, you can extend this waiting period dynamically by sending an event to Pushwoosh. **Example** Suppose a user has entered a **Wait for Trigger** or **Time Delay** step in your journey, where the initial waiting time is set to 1 hour. If you want the user to wait longer (for example, an extra 30 minutes) you can extend the wait time dynamically by sending an event to Pushwoosh with the `extend_wait` parameter. After this extension, the user will remain in the step for a total of 1 hour + 30 minutes (1.5 hours) instead of just the original 1 hour. ## How to configure To extend the waiting time, send an event to Pushwoosh using the [`/postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) method and include the required attributes described below. Note The event name can be arbitrary. ##### Required attributes You must include these attributes in your request for the waiting time extension to work: | Parameter | Type | Description | | ------------- | ------- | --------------------------------------------------------------------------- | | `extend_wait` | integer | Number of seconds to extend the waiting period (e.g., `1800` = 30 minutes). | | `journey_ids` | array | List of Journey IDs where the extension should be applied. | Limitations and rules Maximum waiting time: 90 days from the current moment. ##### Example ```json { "eventName": "YOUR_EVENT_NAME", "userId": "USER_ID_IF_EXISTS", "hwid": "HWID", "attributes": { "extend_wait": 1800, "journey_ids": ["JOURNEY_ID"] } } ``` --- # Webhook integration samples **URL:** https://docs.pushwoosh.com/developer/guides/customer-journey/webhook-samples/ Samples for integration with other services using Webhook --- Using webhooks, you can share customer journey data with just about any other service out there: analytics, CRM systems, marketing automation services, and much more. For example, automatically notify external services when a customer has taken a particular action within the journey, send customer data into your analytic tools, and trigger third-party emails, SMS or WhatsApp messages on specific in-journey events – there are numerous use cases, choose yours. [Learn more about the Webhook element in Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) This post shows how to implement webhooks for different use cases and services. ## Mixpanel ### Use case Send events data from a journey to Mixpanel. ### Prerequisites * Mixpanel account and Project ID * [Service account](https://developer.mixpanel.com/reference/authentication#service-accounts) Webhook for ingestion API (): ### URL example `https://api.mixpanel.com/import?strict=1&project_id=XXXXXXX` ```plaintext Authorization: Basic UHVzaHdvb3NVTTWlrcjROZUE= Content-Type: application/json ``` ### Data example ```plaintext [ { "event": "PW_ApplicationOpen", "properties": { "time": "{{point:48ae746b-0f54-47db-b96c-1f082e110ff4;event:PW_ApplicationOpen;attribute:datetime}}", "distinct_id": "{{device:hwid}}", "$insert_id": "48ae746b-0f54-47db-b96c-1f082e110ff4", "device_type": "{{point:48ae746b-0f54-47db-b96c-1f082e110ff4;event:PW_ApplicationOpen;attribute:device_type}}", "application_version": "{{point:48ae746b-0f54-47db-b96c-1f082e110ff4;event:PW_ApplicationOpen;attribute:application_version}}", "timezone": "{{tag:Timezone}}", "name": "{{tag:Name}}" } } ] ``` ## Amplitude ### Use case Send events data from a journey to Amplitude. ### Prerequisites * Amplitude Account Webhook for Ingestion API (): ### URL `https://api.amplitude.com/2/httpapi` ```plaintext Accept: */*Content-Type: application/json ``` ### Data example ```plaintext {   "api_key": "c506ffde6db9xxxxxxa50120e22ec5",   "events": [     {       "user_id": "{{device:user_id}}",       "device_id": "{{device:hwid}}",       "event_type": "PW_ApplicationOpen",       "user_properties": {"timezone": "{{tag:Timezone}}", "sdk_version": "{{tag:SDK Version}}"},       "country": "{{tag:Country}}"     }   ] } ``` ## Slack ### Use case Trigger Slack messages from a journey. ### Prerequisites * Slack Account ### URL example `https://pushwoosh.slack.com/apps/XXXXXXXX-incoming-webhooks` ```plaintext Content-Type: application/json ``` ### Data example ```json { "text": "Hi there :smile:", "username": "John Doe" } ``` ## Zapier ### Use case Send data to Zapier which can further be used in Zapier automation across 4000+ apps. ### Prerequisites * Zapier Account ### URL example `https://hooks.zapier.com/hooks/catch/XXXXXXX/xxxxxxx` ```plaintext Content-Type: application/json ``` ### Data example ```json { "hwid": "{{device:hwid}}", "sdk": "{{tag:SDK Version}}" } ``` ## Twilio (SMS) ### Use case Send SMS from a journey ### Prerequisites * Twilio Account (access to TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN) * Phone number purchased ### URL example `https://api.twilio.com/2010-04-01/Accounts/\$TWILIO_ACCOUNT_SID/Messages.json` ```text Content-Type: application/x-www-form-urlencoded Authorization: Basic.... // Encode TWILIO_ACCOUNT_SID and TWILIO_AUTH_TOKEN with https://www.blitter.se/utils/basic-authentication-header-generator/ to get Authenticate value ``` ### Data example ```text From=%2B18646511557&To=%2B15852826356&Body=... ``` --- # Introduction to the Guides section **URL:** https://docs.pushwoosh.com/developer/guides/intro-to-guides/ A detailed overview of key platform features, including messaging channels, audience segmentation, customer journey integration, and content personalization, to assist developers in effectively utilizing the platform. --- The **How-to-guides** section offers detailed instructions and best practices to help developers successfully integrate and make the most of the platform’s features. ### Messaging channels Learn how to implement messaging channels across different platforms and use cases: [Create subscription prompts ](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/) [Set up subscription prompts to manage user opt-ins and preferences](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/) [Custom subscription widgets ](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/custom-subscription-widget/) [Build custom subscription widgets tailored to your needs](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/custom-subscription-widget/) [Default subscription widgets ](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/default-widget/) [Use pre-built default subscription widgets](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/default-widget/) [Send push notifications by User ID ](https://docs.pushwoosh.com/developer/guides/messaging-channels/send-a-pushby-userid/) [Target and send push notifications to specific users](https://docs.pushwoosh.com/developer/guides/messaging-channels/send-a-pushby-userid/) [Send silent pushes ](https://docs.pushwoosh.com/developer/guides/messaging-channels/silent-push-notifications/) [Deliver silent push notifications for background processing](https://docs.pushwoosh.com/developer/guides/messaging-channels/silent-push-notifications/) [Create iOS interactive pushes ](https://docs.pushwoosh.com/developer/guides/messaging-channels/ios-interactive-push/) [Build interactive push notifications for iOS devices](https://docs.pushwoosh.com/developer/guides/messaging-channels/ios-interactive-push/) [Send custom data ](https://docs.pushwoosh.com/developer/guides/messaging-channels/using-custom-data/) [Send custom data with your messages](https://docs.pushwoosh.com/developer/guides/messaging-channels/using-custom-data/) [Configure deep links ](https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking/) [Set up deep linking to direct users to specific app locations](https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking/) ### Message inbox Configure and utilize message inboxes for different platforms: [Message inbox overview ](https://docs.pushwoosh.com/developer/guides/message-inbox/) [Learn how to implement message inboxes in your app](https://docs.pushwoosh.com/developer/guides/message-inbox/) [Mobile message inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) [Set up message inboxes for mobile applications](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) [Web message inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) [Implement message inboxes for web platforms](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) ### Audience and segmentation Organize and target your audience with advanced segmentation: [Tags ](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/) [Use tags to segment and organize your audience](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/) [Events ](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/) [Track and respond to user events](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/) [Custom events examples ](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples/) [Access detailed examples of custom events for advanced segmentation](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples/) ### Customer journey Learn how to build and manage customer journeys: [API-based entry ](https://docs.pushwoosh.com/developer/guides/customer-journey/api-based-entry/) [Trigger customer journeys using API requests](https://docs.pushwoosh.com/developer/guides/customer-journey/api-based-entry/) [Webhook integration samples ](https://docs.pushwoosh.com/developer/guides/customer-journey/webhook-samples/) [Integrate webhooks with your customer journeys](https://docs.pushwoosh.com/developer/guides/customer-journey/webhook-samples/) [Extend waiting time in journeys ](https://docs.pushwoosh.com/developer/guides/customer-journey/presence-extension-wait-delay/) [Dynamically extend user waiting periods in the Wait for Trigger and Time Delay elements](https://docs.pushwoosh.com/developer/guides/customer-journey/presence-extension-wait-delay/) ### Personalization Unlock personalization capabilities to enhance user experiences: [Dynamic content ](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/) [Create dynamic, personalized content for your users](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/) [Liquid templates ](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/) [Use Liquid templates for advanced personalization](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/) [Multi-language support ](https://docs.pushwoosh.com/developer/guides/personalization/multi-language/) [Implement multi-language messaging and content](https://docs.pushwoosh.com/developer/guides/personalization/multi-language/) ### Content Customize and enhance rich media templates: [Rich media templates syntax ](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/) [Learn the syntax for creating rich media templates](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/) [Styling rich media ](https://docs.pushwoosh.com/developer/guides/content/customizing-rich-media-appearance/) [Customize the appearance of rich media content](https://docs.pushwoosh.com/developer/guides/content/customizing-rich-media-appearance/) --- # About message inbox **URL:** https://docs.pushwoosh.com/developer/guides/message-inbox/ Reach out to users even if they opt out of push notifications --- Message Inbox creates a special space for important messages in your app. Saving a message to the inbox doesn’t itself trigger a notification or change the app icon badge. Both come from the push payload itself, independent of Message Inbox. The message is delivered and displayed in the app once users open it. With Message Inbox, you can deliver important messages to a wider audience. ### Implementation Message Inbox can be implemented into mobile and web applications. Please follow the dedicated guides: [Mobile Message Inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) [Web Message Inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) Message Inbox can be implemented with a default design or fully customized to your app’s UI. [Customized iOS UI guide ](https://github.com/Pushwoosh/pushwoosh-inbox-ui-ios-sdk) [Customized iOS XCFramework guide ](https://github.com/Pushwoosh/PushwooshInboxUI-XCFramework) [Customized Android UI guide ](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk) This is an example of what the Message Inbox design can look like: ![Example Message Inbox interface showing list of stored push notifications with titles and preview text](https://docs.pushwoosh.com/message-inbox-README-1.webp) ### Statistics Open rate statistics for the Message Inbox are available in the Pushwoosh API. You can also use the Pushwoosh API to receive data on messages from Message Inbox only. To do so, use the [`getMessageLog`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog) API method with the following parameters: `message_id` and `action`: `"inbox_opened"`. [Follow the Pushwoosh Statistics API guide >](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog) --- # Set up mobile message inbox **URL:** https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/ Integrate Pushwoosh Message Inbox in your iOS, Android, or cross-platform app to store and display push messages inside the app. --- Message Inbox (also called App Inbox, the “bell icon” screen inside your app) lets you keep the most important messages in your app by creating a dedicated section for such notifications. Messages can be delivered to the Inbox with a notification or without one. You can choose it in the settings. Note Read more about how [Message Inbox works](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/). Note To implement Message Inbox for websites (Web Inbox), please refer to the [Web Message Inbox guide](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) or the simpler [Web Inbox widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-inbox-widget/) if you just need a drop-in bell icon. ## Setting up the inbox in your app **iOS:** see [Setting up Pushwoosh InboxKit iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/) for the current UIKit inbox screen (card layouts, inline CTA buttons, install via SPM or CocoaPods). **Android:** see [Setting up Pushwoosh Inbox UI Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/setting-up-pushwoosh-inbox-ui-android/) for the `pushwoosh-inbox-ui` module (rich cards, inline CTA buttons, style customization). **Cordova:** see [Setting up Message Inbox for Cordova](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/setting-up-message-inbox-for-cordova/) for `presentInboxUI`, style customization, and programmatic message management. **React Native:** see [Setting up Message Inbox for React Native](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/setting-up-message-inbox-for-react-native/) for `presentInboxUI`, style customization, and programmatic message management. **Flutter:** see [Setting up Message Inbox for Flutter](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/setting-up-message-inbox-for-flutter/) for the `pushwoosh_inbox` package, `PWInboxStyle` customization, and programmatic message management. **Xamarin:** see [Message inbox for Xamarin](https://docs.pushwoosh.com/developer/resources/xamarin/message-inbox-for-xamarin/) (deprecated, Xamarin itself reached end of support in 2024). Using Message Inbox in plugins with ProGuard If your Android app uses ProGuard for code shrinking, you have to add the following rule to your **proguard-rules.pro:** ```plaintext #INBOX -keep public class com.pushwoosh.inbox.PushwooshInboxPlugin { *; } ``` ## Unread messages badge Note This is an in-app badge, for example on your own Inbox icon inside the app’s UI. Neither method touches the app icon badge. No Pushwoosh SDK ties the unread count to the app icon, so if you want it to show there too, your app code needs to set it explicitly. To display the number of unread messages on a badge over an Inbox icon, please use the following API methods: **iOS** `[PWInbox unreadMessagesCountWithCompletion:^(NSInteger count, NSError *error) { }];` **Android** `PushwooshInbox.unreadMessagesCount(result -> { });` --- # Set up web message inbox **URL:** https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/ Set up Web Message Inbox using Pushwoosh Web SDK to store, display, and interact with notifications on your website. --- #### Prerequisites Make sure you’ve implemented Pushwoosh Web SDK on your website. To do so, please follow our [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/). **Message Inbox for websites** allows you to store web push notifications so users can revisit them later, ensuring important messages are seen. ## How it works Here’s a typical workflow with InboxMessages: | Step | Method | | -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | Page load | | | Synchronizing messages with the server | `InboxMessagesModel.updateMessages` Executed while the SDK is initialized | | Deleting outdated messages | `InboxMessagesModel.updateMessages` Executed while the SDK is initialized | | Receiving the number of unread messages to update a badge | `Pushwoosh.pwinbox.unreadMessagesCount` Executed manually (after the `onUpdateInboxMessages`event triggers) | | Loading messages to be displayed in Inbox | `Pushwoosh.pwinbox.loadMessages` Executed manually (after the `onUpdateInboxMessages`event triggers) | | Marking messages got in the viewport as read | `Pushwoosh.pwinbox.readMessagesWithCodes` Executed manually | | Performing an action assigned to the message opened | `Pushwoosh.pwinbox.performActionForMessageWithCode` Executed manually | | Deleting messages by click on a delete icon or swipe-right a message | `Pushwoosh.pwinbox.performActionForMessageWithCode` Executed manually | ## Event listeners onUpdateInboxMessages ```javascript // Executed after the Inbox is updated automatically while the page is loading. Pushwoosh.push(['onUpdateInboxMessages', function(api, pwinbox) { pwinbox.loadMessages().then(function(messages) { console.log('EVENT: onUpdateInboxMessages', messages); }); }]); ``` onPutNewMessageToInboxStore ```javascript // Executed by ServiceWorker after the Inbox Message is received and saved to indexedDB. Pushwoosh.push(['onPutNewMessageToInboxStore', function(api, message) { console.log('EVENT: onPutNewMessageToInboxStore', message); }]); ``` ## Public module **WebSDK InboxMessagesPublic module** presents a public interface to implement Web Inbox. Model ```txt type TInboxMessageTypePlain = 0; type TInboxMessageTypeRichmedia = 1; type TInboxMessageTypeURL = 2; type TInboxMessageTypeDeeplink = 3; type TInboxMessageType = TInboxMessageTypePlain // depends on action_params | TInboxMessageTypeRichmedia | TInboxMessageTypeURL | TInboxMessageTypeDeeplink; type TInboxLayout = 'classic' | 'banner' | 'captioned' | 'carousel'; interface IInboxMessageSlide { imageUrl: string; caption: string; // caption overlaid on the slide; empty when the slide has none link: string; // where a tap on the slide goes; empty when the slide has none, and the message's own action applies instead } interface IInboxMessagePublic { code: string; // inbox_id title: string; // title message: string; // body imageUrl: string; // image sendDate: string; // send_date, ISO 8601 in UTC type: TInboxMessageType; // depends on action_params isRead: boolean; // true if status is "read" or "open" link: string; // Deeplink | URL | "/" isActionPerformed: boolean; // true if status is "open" layout: TInboxLayout; // cell layout to draw, already degraded to one this message can render iconUrl: string; // small round avatar; same value as imageUrl, under the name the layouts use heroUrl: string; // hero image; falls back to iconUrl when the message has no banner, so it's set for classic messages too carousel: Array; // slides of the carousel layout; empty for every other layout customData: Record; // the campaign's own custom data, as sent from the control panel } ``` sendDate is now a true UTC instant (v3.75.0+) `sendDate` used to carry local wall-clock time labeled `Z`. Code that compensated for the old value by applying a timezone offset will now be off by that same offset — drop the compensation. The built-in widget is unaffected. **Cell layouts.** A message renders as one of four layouts — `classic`, `banner`, `captioned`, or `carousel` — the same layouts the campaign preview in the control panel shows. A layout that can’t render with what the message carries degrades to `classic`: `banner` needs a hero image, `captioned` needs a hero image plus both a title and a body, and `carousel` needs at least one slide plus both a title and a body. `layout` on the public message is always the one actually drawn, already degraded. Checking `layout` is the only reliable way to tell what a message will render as — `heroUrl` is set for `classic` messages too (see the field note above), so an integration must not use its presence to decide whether to draw a hero. ## Public methods Check out public methods’ descriptions in the [Web Push SDK 3.0](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/) guide. `Pushwoosh.pwinbox.syncMessages()` pulls the current inbox from the server; call it to force a refresh outside the built-in widget’s own polling. --- # Configure deep links **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking/ Learn how to configure deep links in iOS, Android, and Cordova apps to direct users to specific app sections via push messages. --- Note #### Compatible with iOS, Android and Windows, Cordova, Phonegap By default, a tap on a push message opens an app’s home screen. Deep Links open a specified section of the app once a user taps on a push or in-app message. Highlight the new content, promotional pages, or sale offers by taking users there directly from your message. Boost conversions and improve user experience with Deep Linking! This tutorial explains how to configure Deep links. ## Configuring deep links ### iOS Use the following [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#deep-linking) to set up Deep Linking for iOS ### Android For Android, please follow this [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#deep-linking) ### PhoneGap / Cordova For applications developed with PhoneGap / Cordova, we recommend using [**this plugin**](https://github.com/EddyVerbruggen/Custom-URL-scheme). It’s super easy to set deep links for Cordova and it works with Pushwoosh deep linking functionality out of the box. First, you should add the plugin to your app and specify a scheme you want your app to listen to: ```plaintext $ cordova plugin add cordova-plugin-customurlscheme --variable URL_SCHEME=pushwoosh ``` This will set a deep link for **pushwoosh://** scheme. **Usage** 1. Your app can be launched by linking to it like this from a website or an email for example (all of these will work): ```txt Open my app Open my app Open my app Open my app ``` **pushwoosh** is the value of URL_SCHEME you used while installing this plugin. 2. If you’re trying to open your app from another PhoneGap app, use the InAppBrowser plugin and launch the receiving app like this, to avoid a ‘protocol not supported’ error: ```txt ``` More detailed information on plugin usage you can find in [plugin documentation](https://github.com/EddyVerbruggen/Custom-URL-scheme#3-usage). To learn how to create and send Deep links from the Pushwoosh Control Panel, please refer to the [product documentation](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) or share the link with your marketer. --- # Create in-apps with JavaScript **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/inapp-with-javascript/ Use deeply contextual in-apps to capitalize on user engagement --- In-app messages are displayed directly within the app interface while a user is already interacting with an app. As a result, users perceive in-apps as less intrusive than push notifications, emails or SMS. In-app messages always contain images or videos called Rich Media. In-apps can also include formatted text, links, buttons with CTAs, and other data, allowing you to communicate with your app users in engaging forms. In-Apps are HTML-based and support JavaScript. Pushwoosh SDK injects `pushwoosh` variable that provides the following API: You can use [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method to send a new event directly from In-App JavaScript code. ```javascript pushwoosh.postEvent( 'eventName', { TestAttributeString: 'testString', TestAttributeInt: 42, TestAttributeList: [123, 456, 'someString'], TestAttributeBool: true, TestAttributeNull: null, TestAttributeDaysAgo: 7, TestAttributeDate: new Date() }, function() { console.log('Post event success') }, function(error) { console.log('Post event failed: ', error) } ) ``` You can also use `sendTags` method to set tags for the device from an in-app: ```javascript pushwoosh.sendTags({ IntTag: 42, BoolTag: true, StringTag: 'testString', ListTag: ['string1', 'string2'] }) ``` To get device tags, use the `getTags` method as follows: ```javascript pushwoosh.getTags( function(tags) { console.log('tags: ' + JSON.stringify(tags)) }, function(error) { console.log('failed to get tags: ' + error) } ) ``` If you want to close an in-app from the JavaScript code call `closeInApp()` method: ```plaintext pushwoosh.closeInApp(); ``` Or simply use custom scheme URL for the button/link `` In the [built-in in-app editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#close-in-app), a button can close the in-app without code: set its **Action Type** to **Close In-App**. To request the push permission from inside an in-app (for example, a pre-permission message shown before the system prompt), call `registerForPushNotifications()`: ```javascript pushwoosh.registerForPushNotifications(); ``` The method takes no arguments and triggers the platform’s native permission prompt. On iOS, the SDK also closes the in-app automatically once you call it, so a further `closeInApp()` call is optional and harmless. On Android, the in-app stays open until you call `closeInApp()` yourself. When sending a push notification with Rich Media, you can get custom data of that push with the following code: `pushwoosh.getCustomData()` ### Custom JavaScript interface In-App Messages JavaScript functionality can be unlimitedly extended by exposing native Java/Objective-C/Swift methods to JavaScript. This can be achieved with `addJavascriptInterface` methods of Pushwoosh SDK for [iOS](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/iOS_SDK/Pushwoosh/PushwooshCore/InAppMessages/PWInAppManager.h) and [Android](https://github.com/Pushwoosh/pushwoosh-android-sdk/blob/master/Sources/pushwoosh/src/main/java/com/pushwoosh/inapp/InAppManager.java). --- # Create iOS interactive push **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/ios-interactive-push/ Set up interactive push on iOS by configuring categories in Pushwoosh and handling responses in your app’s delegate. --- iOS 8 introduced interactive notifications, allowing users to take actions directly from the notification banner. Pushwoosh now offers **iOS Categories**, enabling you to create custom buttons within the Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-interactive-push) ## API When your application calls [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice), Pushwoosh API returns the response that contains a list of available Categories with their IDs and details for each button as follows: ```json { "status_code": 200, "status_message": "OK", "response": { "iosCategories": [ { "categoryId": 65, "buttons": [ { "id": 0, "label": "Rate", "type": "1", "startApplication": 1 }, { "id": 1, "label": "Later", "type": "0", "startApplication": 0 } ] } ] } } ``` These Categories are now available on the device, so they can be properly displayed when a message arrives and your application is not running in the foreground. In order to send your push with a category from the Pushwoosh Journey, simply select it in the iOS platform settings while composing your message. In case you are sending your pushes remotely through Pushwoosh API, in the [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) requests you should use the `ios_category_id` parameter with a corresponding Category ID as a value: ```json { "ios_category_id": "65" // Optional. String value. iOS8 category ID from Pushwoosh } ``` When a push message containing a category ID arrives, Pushwoosh SDK displays the notification with a set of buttons this category contains. ## Buttons & actions in Pushwoosh iOS SDK In order to perform various actions upon opening an app, you should create a custom `UNUserNotificationCenterDelegate` implementation and override its `didReceiveNotificationResponse` method: #### CustomDelegate ```swift class CustomDelegate: NSObject, UNUserNotificationCenterDelegate { func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { let identifier = response.actionIdentifier let category = response.notification.request.content.categoryIdentifier if category == "10212" { if identifier == "1" { // DO SOMETHING } else { // DO SOMETHING ELSE } } completionHandler() } } ``` Where `identifier` is a button ID, and `category` is derived from the notification payload. Then, create an instance of this class and pass it to Pushwoosh SDK using the proxy method: #### AppDelegate ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Pushwoosh.configure.registerForPushNotifications() let customDelegate = CustomDelegate() Pushwoosh.configure.addNotificationCenterDelegate(customDelegate) return true } ``` ![iOS interactive push notification with custom action buttons displayed in the notification banner](https://docs.pushwoosh.com/messaging-channels-ios-interactive-push-1.png) --- # Send a push by User ID **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/send-a-pushby-userid/ Learn how to send a push notification to a specific user by their User ID --- To send a push to a list of users, you can either use [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) API call, or upload a CSV file with the User IDs. ## Sending a push by User ID via API ### createMessage Tip Please note that [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) parameters are fully customizable, so you can create a highly tailored push message. ### Request body parameters | Parameter | Description | | ----------- | ---------------------------------------------------------------------------------------------------- | | application | Your Pushwoosh application ID where you send the message to | | auth | API access token from the Pushwoosh Control Panel | | send_date | Set the time you want the message to be sent (in UTC) or use ‘now’ to send it immediately. | | content | The text for your push notification. | | users | A list of User IDs your push message is sent to. You can send a push to up to 1000 User IDs at once. | ### Request example ```plaintext { "request": { "application": "APPLICATION_CODE", "auth": "API_ACCESS_TOKEN", "notifications": [{ "send_date": "now", "content": "Hello world!", "users":["external_user_1"] }] } } ``` ### Response example ```plaintext { "status_code":200, "status_message":"OK", "response": { "Messages":["{Notification code}"] } } ``` --- # Send a silent push **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/silent-push-notifications/ Learn how to send a push from your app without notifying users --- Sometimes you need to pass some data to the app, update the app in the background, or get new data from your server without notifying users. This is where **Silent Push Notifications** become very useful! Silent Push Notifications are delivered to users’ devices without an alert or sound. The badge value in the payload isn’t stripped. Only its relative **+1**/**-1** increment doesn’t apply, since that relies on the [Notification Service Extension](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges/), which iOS only launches for pushes that include an alert. When a silent push arrives, the app is woken up in the background. Users don’t receive any alerts or see any push content. By sending Silent Push Notifications, you can: * Notify your app about new content available to download * Perform background tasks * Get new data from your server * Pass custom data to the app Silent Push Notifications are available out-of-the-box for all Pushwoosh users and can be implemented easily through the API or [Pushwoosh Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/). Silent Push Notifications are also useful for cleaning up your user base. Each time a push is sent, invalid or non-existent push tokens are removed from the database. This is why Silent Push Notifications are used in **Uninstall Tracking**, which helps keep your user base valid and updated. ### Sending silent push notifications To send Silent Push Notifications via the [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) API request, set the `"ios_silent"` and `"android_silent"` parameters to `1`. #### Example API request: ```plaintext POST https://api.pushwoosh.com/json/1.3/createMessage { "request": { "application": "XXXXX-XXXXX", "auth": ACCESS_TOKEN, "notifications": [{ "send_date": "now", "ignore_user_timezone": true, "content": "test", "platforms": [1, 3], "ios_silent": 1, "android_silent": 1 }] } } ``` --- # About Subscription prompt widgets **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/ Set up Subscription Prompt widgets to show native permission dialogs and improve push notification opt-in rates. --- Superseded by the Subscription widget The [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/) replaces both widgets on this page. On a panel-managed application it takes over automatically — the Subscription Prompt no longer loads there, and its Control Panel settings are not carried over. This page still applies to a snippet-configured application using `autoSubscribe`. Due to the latest Chrome and Firefox updates, the `autoSubscribe` [WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30) method no longer triggers the native permission request pop-up. * In the latest versions of Firefox, the browser shows the quiet permission prompt whenever a website prompts for permission, no initial user action on the page required (like clicking on a button). * In [Chrome](https://blog.chromium.org/2020/01/introducing-quieter-permission-ui-for.html), quieter notification prompts will be enabled first for users who, in most cases, block notification opt-in requests and second, on sites with low opt-in rates. To prevent the high rate of blocking push notifications on your website, we no longer call the native prompt by `autoSubscribe`. To continue subscribing users to web push notifications, use the Subscription Prompt widgets triggering the native permission request. 1. Use the `autoSubscribe:true` parameter while initializing WebSDK. 2. Then, customize the widget in your Control Panel and publish it — users will see the widget once your site is loaded and can agree or disagree to opt-in. If they do agree, the native prompt will be displayed. There are two options available: * [**Default widget**](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/default-widget/) simply triggering the native subscription prompt; * [**Custom widget**](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/custom-subscription-widget/) allowing you to create a unique user experience fully fitting your website. --- # How to create a custom subscription widget **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/custom-subscription-widget/ Create a Subscription Prompt widget that fully fits your website --- ## Creating a subscription widget If the default widget does not match your goals and preferences, you can create your custom subscription widget by following the steps described below. 1. Add to your project a hidden `
` containing the prompt to subscribe. ```txt
Don't miss anything important — subscribe to push notifications!
You're subscribed
``` 2. Specify its styles. ```txt ``` 3. Once the Pushwoosh WebSDK is initialized, check whether a user is subscribed already or has blocked permission requests. If not, display the widget. Caution Make sure to display widget AFTER the WebSDK is fully initialized. Otherwise, the widget won’t work correctly. ```txt ``` --- # How to create a default widget **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/default-widget/ Add a default push widget with WebSDK 3.0 to trigger native prompts and manage opt-ins with minimal code to boost engagement and simplify subscriptions. --- The default subscription widget is aimed to encourage your website visitors to subscribe to push notifications. Being shown to users due to your website’s logic, the widget gently prompts website visitors to allow receiving push notifications from you, thus creating long-time relationships with prospects and customers. ## How it works Default widget contains the plain text explaining the value of push notifications and two buttons to agree or disagree on receiving pushes. Once a user presses the Confirmation button, the native subscription prompt is triggered. Note **Frequency capping** If a user clicks “Deny” or ignores the default widget, our SDK will show the default widget again **4 hours later**. The widget is capped to **3 total impressions**. If a user denies three times in a row, they will not see the widget anymore and should change their opt-in preferences manually from browser settings. ## How to implement ### Integration To implement the widget on your website, integrate the WebSDK 3.0 following the [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/), then set up the widget. Important Make sure to use the `autoSubscribe:true` parameter while initializing the WebSDK. ### Creating a default widget You can set up the default widget in the Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/default-widget/#create-a-default-widget) or share the link with your marketing team. --- # Send custom data **URL:** https://docs.pushwoosh.com/developer/guides/messaging-channels/using-custom-data/ Learn how to handle custom push data in iOS apps. Trigger actions, update UI, run A/B tests, and change icons using JSON payloads and Pushwoosh SDK. --- This guide will help you to better understand how to pass custom data to your apps in the push notification payload so your app could react to such pushes and perform various actions accordingly. The ways you handle custom data can vary according to your business objectives. In this article we’ll show some basic examples of parsing custom data and performing simple actions: * changing the background color of the app; * opening a custom page in your app. ## Prerequisites This guide covers iOS Native development. It is assumed that you have an iOS sample application configured and receiving push notifications as per the [iOS Getting Started guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/quick-start/). In `AppDelegate` in the `didFinishLaunchingWithOptions` function, we will use `self.viewController` as a delegate for push notifications processing: ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Pushwoosh.sharedInstance().delegate = self.viewController Pushwoosh.sharedInstance().registerForPushNotifications() if let launchOptions = launchOptions { Pushwoosh.sharedInstance().handlePushReceived(launchOptions) } return true } ``` Our `ViewController` implements the `PWMessagingDelegate` protocol: ```swift extension ViewController: PWMessagingDelegate ``` And therefore has the `onMessageOpened` function that handles received push notifications: ```swift // User pressed on the push notification func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) {} ``` ## Opening another ViewController and changing the background color Now we are getting `customData` from the push payload. For instance, let’s change the view’s background color and discount percentage. We assume that the custom data will contain the items `“r”`, `“g”`, `“b”`, and `"d"` in JSON object format as follows: ```swift guard let customDataJson = message.customData, let redString = customDataJson["r"] as? String, let greenString = customDataJson["g"] as? String, let blueString = customDataJson["b"] as? String, let discount = customDataJson["d"] as? String else { return } setViewBackgroundColor(red: redString, green: greenString, blue: blueString, discount: discount) ``` We will use the following function to open a new ViewController and set the background color and discount percentage: ```swift func setViewBackgroundColor(red: String, green: String, blue: String, discount: String) { let red = CGFloat((red as NSString).floatValue) let green = CGFloat((green as NSString).floatValue) let blue = CGFloat((blue as NSString).floatValue) let color = UIColor(red: red / 255.0, green: green / 255.0, blue: blue / 255.0, alpha: 1) if let topController = UIApplication.shared.topMostController() { let storyboard = UIStoryboard(name: "Main", bundle: nil) if let customViewController = storyboard.instantiateViewController(withIdentifier: "custom_page") as? CustomPageViewController { customViewController.discount = discount customViewController.bgColor = color topController.present(customViewController, animated: true, completion: nil) } } } ``` Let’s test our example. Navigate to Pushwoosh Journey and add a **Push element** to the canvas. Next, click on **Create new content**. In the Push content form that opens, enter any push notification text. ![Push notification creation form in Pushwoosh Control Panel with text field and custom data toggle](https://docs.pushwoosh.com/messaging-channels-using-custom-data-1.webp) Then toggle `Send custom data` on. Insert the JSON into the Custom data field. As we’ve decided that our custom data format would be a JSON object with `“r”`, `“g”`, `“b”` values in it, we need to use the `“custom data”` field in the Control Panel and populate it with the JSON object `{"r":"30", "g":"144", "b":"255", "d":"25"}`: ![Custom data field in push notification form showing JSON object with RGB color values and discount parameter](https://docs.pushwoosh.com/messaging-channels-using-custom-data-2.webp) Upon tapping the push notification, the `CustomPageViewController` opens, the background color is set, and the discount is applied: ![Custom page view controller displayed with background color and discount percentage applied from push notification custom data](https://docs.pushwoosh.com/messaging-channels-using-custom-data-3.webp) CustomPageViewController Code: ```swift class CustomPageViewController: UIViewController { var bgColor: UIColor? var discount: String? @IBOutlet weak var titleLabel: UILabel! override func viewDidLoad() { super.viewDidLoad() self.view.backgroundColor = self.bgColor if self.discount != nil { self.titleLabel?.text = "ONLY TODAY GET \(self.discount!)% DISCOUNT!" } } func showPromoPage(discount: String) { let vc = CustomPageViewController() vc.bgColor = self.view.backgroundColor vc.discount = discount vc.modalTransitionStyle = UIModalTransitionStyle.crossDissolve if self.presentedViewController != nil { self.dismiss(animated: true, completion: { self.present(vc, animated: true, completion: nil) }) } else { self.present(vc, animated: true, completion: nil) } } @IBAction func closeButtonAction(_ sender: Any) { self.dismiss(animated: true) } } ``` We assume that the discount value will come as `"d"` parameter in the JSON of the push notification payload. As push notification payloads are limited in size, you’d rather use short names for the parameters. ```swift guard let customDataJson = message.customData, let redString = customDataJson["r"] as? String, let greenString = customDataJson["g"] as? String, let blueString = customDataJson["b"] as? String, let discount = customDataJson["d"] as? String else { return } ``` You can write code to initialize and open different View Controllers depending on the parameters you pass in the push notification payload. ## Opening A/B testing view controller Let’s consider another use case for custom data in a push notification. For instance, we need to open one view controller for one user segment and another view controller for a different user segment. Simply put, we can use custom data for A/B testing in your application. Let’s create two view controllers. One controller (A) will open with a discount value, and the second controller (B) will open with a different discount parameter. Now let’s write the code that will open the appropriate view controller based on the push notification payload: ```swift func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { // MARK: - A/B Testing via Custom Data guard let customDataJson = message.customData, let viewController = customDataJson["vc"] as? String else { return } if viewController == "A" { setControllerA() } else if viewController == "B" { setControllerB() } } func setControllerA() { if let topController = UIApplication.shared.topMostController() { let storyboard = UIStoryboard(name: "Main", bundle: nil) if let customViewController = storyboard.instantiateViewController(withIdentifier: "a_vc") as? AViewController { customViewController.discountA = "50" topController.present(customViewController, animated: true, completion: nil) } } } func setControllerB() { if let topController = UIApplication.shared.topMostController() { let storyboard = UIStoryboard(name: "Main", bundle: nil) if let customViewController = storyboard.instantiateViewController(withIdentifier: "b_vc") as? BViewController { customViewController.discountB = "100" topController.present(customViewController, animated: true, completion: nil) } } } ``` Add your JSON to the Custom data field. ```plaintext { "vc": "A" } // Choose your Custom Data (A or B) { "vc": "B" } ``` Depending on the custom data you sent in the push payload, either `'vc': 'A'` or `'vc': 'B'`, one segment of users will open one controller, while another segment of users will open a different controller. ### View Controller “A” ![View Controller A displaying discount value for A/B testing segment](https://docs.pushwoosh.com/messaging-channels-using-custom-data-4.webp) ### View Controller “B” ![View Controller B displaying different discount value for A/B testing segment](https://docs.pushwoosh.com/messaging-channels-using-custom-data-5.webp) ## Changing app icon via push notification Another example of how we can use push notifications in iOS is to change the app icon. First, let’s add three different icons to the assets. One will be used as the default app icon, while the other two will change based on the custom data in the push notification. ![Xcode assets catalog showing three different app icons for dynamic icon switching](https://docs.pushwoosh.com/messaging-channels-using-custom-data-6.webp) To enable alternate app icons in your iOS application, you need to add the necessary configuration in the `Info.plist` file. Here are the steps to do that: ```xml CFBundleIcons CFBundleAlternateIcons AppIcon-2 CFBundleIconFiles Icon-2 UIPrerenderedIcon AppIcon-3 CFBundleIconFiles Icon-3 UIPrerenderedIcon CFBundlePrimaryIcon CFBundleIconFiles Icon-1 UIPrerenderedIcon ``` In the `pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage)` callback, you can add the code to change the app icon based on the custom data received in the push notification. Here’s how you can implement this: ```swift func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { // MARK: - Change the app icon dynamically guard let customDataJson = message.customData, let appIcon = customDataJson["i"] as? String else { return } UIApplication.shared.setAlternateIconName(appIcon) { error in if let error = error { print(error.localizedDescription) } else { print("Success!") } } } ``` Add the JSON code: ```plaintext { "i": "AppIcon-2" } ``` ![Custom data field in push notification form with JSON object specifying alternate app icon name](https://docs.pushwoosh.com/messaging-channels-using-custom-data-7.webp) When tapping on the push notification, the system will prompt you to change the app icon on the device. ![iOS system prompt dialog asking user permission to change the app icon on the device](https://docs.pushwoosh.com/messaging-channels-using-custom-data-8.webp) You can find the project utilizing custom data on [GitHub](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/customdata/newdemo). --- # Using Dynamic content **URL:** https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/ Learn how to use Dynamic Content in API push messages to personalize payloads with tags, default values, and format modifiers for a better user experience. --- Dynamic Content allows you to personalize your push messages by modifying the message content with corresponding Tag values that you collect from your users. This allows you to address your user by name, and use any of his or her preferences in the message to make it relevant for every single user. ### Personalizing messages sent via API with Dynamic Content To personalize messages sent via API with Dynamic Content, please ensure you use the appropriate syntax for Dynamic Content placeholders. **1.** To reach out to non-tagged users with a message containing some default Tag value, use the following Dynamic Content syntax: `{Tag_name|format_modifier|default_value}`. > For example, the message “Hello `{Alias|CapitalizeFirst|friend}`!” will be sent to users with no alias specified and will look as “Hello friend!”. Important Please note that the 2 and 3 options differ by a single ”|” character but behave in all the contrary way to each other. **2.** To ignore non-tagged users, add a Dynamic Content formatted as follows: `{Tag_name|format_modifier}`. When there is no closing ”|” character, *Ignore non-tagged* is considered to be checked. > For example, a message “Get a -20% coupon in `{Restaurant|CapitalizeFirst}` now!” will be sent only to those users who have any Tag value set on their devices or specified in “dynamic_content_placeholders” parameter of an API request. Non-tagged users won’t receive any message. **3.** To send a message to non-tagged users, format Dynamic Content as follows: `{Tag_name|format_modifier|}`. When there is a closing ”|” character, *Ignore non-tagged* is considered to be unchecked. > For example, a message “Get a -20% coupon in `{Restaurant|CapitalizeFirst|}` now!” will be sent to all the audience targeted including users with no Tag values specified. As a user has no Tag value and no default value is specified, non-tagged users will receive a message as is — “Get a -20% coupon in `{Restaurant|CapitalizeFirst|}` now!”. ## Results As a result, imaginary Klaus and Annie will receive push messages from the same campaign, but personalized with their names and preferences: ![Example push notifications showing personalized messages for Klaus and Annie with their names and preferences](https://docs.pushwoosh.com/personalization-dynamic-content-1.webp) --- # Using Liquid templates **URL:** https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/ Learn how to use content templates in your /createMessage API requests --- Liquid Templates significantly broaden Pushwoosh’s personalization capabilities by implementing sophisticated logic in addition to regular[ Dynamic Content](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/) usage. Message personalization in Pushwoosh is based on [Tags (user data)](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/). Pushwoosh offers a variety of [default Tags](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/#default-tags) and [custom Tags](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags/#custom-tags). Using them, you can specify user’s first name, city, purchase history, etc. to send a more personalized message, for example: Hi `{First_name}`, thanks for ordering `{item}`. Liquid templates add more logic to dynamic content. For instance, if a user’s subscription tag contains “free,” you can send them a message: “Grab your 10% discount.” Modifying the message content according to users’ IDs, behaviors, and preferences is the most efficient way to increase relevance and get more impressive results from your marketing campaigns. ## Syntax Content templates based on [Liquid by Shopify](https://shopify.github.io/liquid/) use a combination of [**tags**](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/#tags), [**objects**](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/#objects), and [**filters**](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates/#filters) to load dynamic content. Content templates allow you to access certain variables from within a template and output their data without having to know anything about the data itself. Note To learn more about the syntax, please refer to [Liquid documentation](https://shopify.github.io/liquid/basics/introduction/). ### Objects `objects` define the content that will be displayed to a user. `objects` should be enclosed in double curly braces: `{{ }}` For example, when personalizing a message, send `{{Name}}` in its body to add the users’ names to the message’s content. The user’s name (Name tag value) will replace the Liquid object in a message the user will see. * Input ```plaintext Hi {{Name}}! We're glad you're back! ``` * Output Hi Anna! We’re glad you’re back! To insert the recipient’s User ID, write `{{userid}}` in lowercase. Variable names are case-sensitive, so `{{user_id}}` and `{{UserId}}` are unknown names and render as empty text. If a recipient has no User ID, `{{userid}}` also renders as empty text and the message is still sent. To show a fallback instead, add the `default` filter: `{{userid | default: "friend"}}`. ### Tags `tags` create the logic and control flow for templates. The curly brace percentage delimiters `{%` and `%}` and the text that they surround do not produce any visible output when the template is rendered. This lets you assign variables and create conditions or loops without showing any of the Liquid logic to a user. For example, using the `if` tag, you can vary the message’s language based on what language is set on user’s device: * Input ```liquid {% if Language == 'fr' %} Salut! {% else %} Hello! {% endif %} ``` * Output (fr) Salut! * Output (es) Hello! ### Tags operators | Operator | Description | | :--------: | -------------------------------------------------------------------------- | | `==` | equals | | `!=` | does not equal | | `>` | greater than | | `<` | less than | | `>=` | greater than or equal to | | `<=` | less than or equal to | | `or` | logical or | | `and` | logical and | | `contains` | checks for the presence of a substring inside a string or array of strings | Note In tags with more than one `and` or `or` operator, operators are checked in order *from right to left*. You cannot change the order of operations using parentheses — parentheses are invalid characters in Liquid and will prevent your tags from working. ### Filters `filters` modify the output of a Liquid object or variable. They are used within double curly braces `{{ }}` and variable assignment, and are separated by a pipe character `|`. Multiple filters can be used on one output, and are applied from left to right. * Input ```plaintext {{ Name | capitalize | prepend:"Hello " }} ``` * Output Hello Anna ## Using Liquid Templates in messages sent via API Use the Liquid syntax in your [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) requests to implement Liquid Templates. Templates are available for the “content” parameter of `createMessage` request, as well as for any other parameter supporting Dynamic Content, in particular, platform-specific “title”, “subtitle”, and “image” params. By using content templates, you can either specify the data in your API requests (passing the “template_bindings” parameter) or get the data from Tag values stored on users’ devices (by not using the “template_bindings” parameter). This way, you’re able to build user-based push campaigns containing extremely relevant content. Note Please note, that unlike Dynamic Content, the variables in templates should be enclosed into double curly braces as follows: `{{myVariable}}`. To define the template logic using the Tags with spaces in their names, use the following technique: **Example** ```plaintext {% capture my_tag %}{{My Tag}}{% endcapture %} {% if my_tag == 'value' %} Content to send in this case {% else %} Content to send otherwise {% endif %} ``` ## Liquid Templates use cases Here you’ll find several use cases when Liquid Templates come in handy. ### Multi-language push Liquid Templates make it possible to definitely specify in what language users should receive your push messages. Look at the simple example of the API request and the message received depending on template bindings used in the request. * Liquid input ```plaintext {% if Language == 'es' %} ¡Hola! {% else %} Hello! {% endif %} ``` * API request ```javascript { "request": { "application": "XXXXX-XXXXX", // Pushwoosh app code "auth": "yxoPUlw.....IyEX4H", // API access token from Pushwoosh Control Panel "notifications" : [ // push message parameters { "content": "{% raw %} {% if language == 'es' %}¡Hola!{% else %}hello!{% endif %} {% endraw %}", "template_bindings": { // optional. When no template_bindings are passed in a request, Tag values from the device are used. "language" : "es" } } ] } } ``` * Output **Language is ‘es’**: ¡Hola! **Language is ‘en’**: Hello! ### Subscription upgrade prompt Encourage your customers to upgrade their subscription based on their current plan. * Liquid input ```plaintext {% if Subscription == 'Basic' %} Upgrade to Silver for getting more product features and 24/7 support. {% elsif Subscription == 'Silver' %} Upgrade to Gold for priority support and advanced features. {% else %} Please contact your manager to renew your subscription. {% endif %} ``` * API request ```json { "request": { "application": "XXXXX-XXXXX", // Pushwoosh app code "auth": "yxoPUlw.....IyEX4H", // API access token from Pushwoosh Control Panel "notifications" : [ // push message parameters { "content": "{% raw %} {% if Subscription == 'Basic' %}Upgrade to Silver for getting more product features and 24/7 support.{% elsif Subscription == 'Silver' %}Upgrade to Gold for priority support and advanced features.{% else %}Please contact your manager to renew your subscription. {% endif %} {% endraw %}", "template_bindings": { // optional. When no template_bindings are passed in a request, Tag values from the device are used. "language" : "es" } } ] } } ``` * Output **For users with Basic subscription plan:** Upgrade to Silver for getting more product features and 24/7 support. **For users with Silver subscription plan:** Upgrade to Gold for priority support and advanced features. **For users with other plans:** Please contact your manager to renew your subscription. ### List tags Content templates are quite helpful to handle Tags of a List type. #### Variable size One of the possible use cases is to deliver different content depending on the number of values the Tag contains. For example, you can provide different discounts to customers with different behaviors. Let’s say the customer has some items in their WishList—encourage them to purchase with the most fitting discount based on how many products they’re going to buy! * Liquid input ```plaintext {% if WishList.size >= 3 %} Get 20% off your next purchase! {% elsif WishList.size == 2 %} Get a 10% discount on your next purchase! {% else %} Hey, take a look at new outwears! {% endif %} ``` * API request ```javascript { "request": { "application": "XXXXX-XXXXX", // Pushwoosh app code "auth": "yxoPUlw.....IyEX4H", // API access token from Pushwoosh Control Panel "notifications" : [ // push message parameters { "content": "{% raw %} {% if WishList.size >= 3 %}Get 20% off your next purchase!{% elsif WishList.size == 2 %}Get a 10% discount on your next purchase!{% else %}Hey, take a look at new outwears!{% endif %} {% endraw %}", "template_bindings": { "WishList" : ["Skinny Low Ankle Jeans", "Linen Trenchcoat", "High Waisted Denim Skirt", "Strappy Tiered Maxi Dress"] } } ] } } ``` * WishList size ≥ 3 ![Email preview with wishlist size greater than or equal to 3](https://docs.pushwoosh.com/personalization-liquid-templates-1.webp) * WishList size = 2 ![Email preview with wishlist size equal to 2](https://docs.pushwoosh.com/personalization-liquid-templates-2.webp) #### Variable contains One more case you might need to cover is to deal with List Tags values and deliver the most relevant content based on what values the Tag contains. * Liquid input ```plaintext {% if WishList contains 'Skinny Low Ankle Jeans' %} Get 20% off products in your wishlist! {% else %} Hey, take a look at the brand new Skinny Low Ankle Jeans! {% endif %} ``` * API request ```javascript { "request": { "application": "C90C0-0E786", "auth": "yxoPUlw.....IyEX4H", // API access token from Pushwoosh Control Panel "notifications" : [ // push message parameters { "content": "{% raw %} {% if WishList contains 'Skinny Low Ankle Jeans' %}Get 20% off your next purchase!{% else %}Hey, take a look at the brand new Skinny Low Ankle Jeans!{% endif %} {% endraw %}", "template_bindings": { "WishList" : ["Skinny Low Ankle Jeans", "Linen Trenchcoat", "High Waisted Denim Skirt", "Strappy Tiered Maxi Dress"] } } ] } } ``` * Variable contains data ![Personalized template with data](https://docs.pushwoosh.com/personalization-liquid-templates-3.webp) * Variable does not contain data ![Fallback view when data is missing](https://docs.pushwoosh.com/personalization-liquid-templates-4.webp) ### Plurals By using the content templates, you’re able to adjust message content according to users’ behavior. For example, you can modify the message text to contain plural words in case the List Tag contains more than one value. * Liquid input ```plaintext Get 20% off item {% if WishList.size > 1 %} s in your WishList! {% else %} in your Wishlist! {% endif %} ``` * API request ```javascript { "request": { "application": "C90C0-0E786", "auth": "yxoPUlw.....IyEX4H", // API access token from Pushwoosh Control Panel "notifications" : [ // push message parameters { "content": "Get 20% off item{% raw %} {% if WishList.size > 1 %}s in your WishList!{% else %} in your Wishlist!{% endif %} {% endraw %}", "template_bindings": { // optional. When no template_bindings are passed in a request, Tag values from the device are used. "WishList" : ["Skinny Low Ankle Jeans", "Linen Trenchcoat", "High Waisted Denim Skirt", "Strappy Tiered Maxi Dress"] } } ] } } ``` * Plural ![Plural form template example](https://docs.pushwoosh.com/personalization-liquid-templates-5.webp) * Singular ![Singular form template example](https://docs.pushwoosh.com/personalization-liquid-templates-6.webp) ### Timezone Template for timezones converts the date and time according to the timezone specified. * Liquid input ```plaintext {{ MyDate | timezone: MyTimezone | date: \"%Y-%m-%d %H:%M\" }} ``` * API request Example ```javascript { "request" : { "auth" : "3H9bk8w3.....Acge2RbupTB", // API access token from Pushwoosh Control Panel "application" : "XXXXX-XXXXX", // Pushwoosh app code "notifications" : [ // push message parameters { "content": "Current Date: {{ MyDate | timezone: MyTimezone | date: \"%Y-%m-%d %H:%M\" }}", "template_bindings": { // optional. When no template_bindings are passed in a request, Tag values from the device are used. "MyDate" : "2019-07-23 15:00", "MyTimezone" : "Asia/Dubai" } } ] } } ``` * Output ![Personalized date output in push notification](https://docs.pushwoosh.com/personalization-liquid-templates-7.webp) ## Connected content Connected content is a feature in Liquid templates that allows you to dynamically retrieve and use data from an external source, such as a web service, directly within your email or push notification messages. This feature enables real-time personalization by fetching JSON data from a specified URL and saving it to a variable that can be utilized in your content. #### Key use cases * **Product recommendations**: Display personalized product lists tailored to each user. * **Promo codes**: Insert unique promo codes generated by a backend service. #### Prerequisites * To use Connected Content, you must have your own backend service that generates and provides the required data (e.g., promo codes, product recommendations) based on **User ID, HWID, or custom tags**. Pushwoosh then fetches this data before sending a message. ### Step-by-step implementation guide #### Step 1. Set up backend service The backend service should: * Accept a request containing user-specific parameters (e.g., `userId`). Connected Content supports `UserID`, `HWID`, or any custom tags you’ve set up in your project. * Return a JSON response with the required data. This content can then be inserted dynamically into messages How it works The backend service acts as a data provider, responding to HTTP requests with user-specific information. 1. Pushwoosh sends a request to your backend, passing user-specific identifiers as query parameters. 2. Your backend processes the request and retrieves the requested data. 3. Your backend returns a JSON response. 4. Before sending a message, Pushwoosh fetches the JSON response from the backend service and uses the returned values (e.g., the `code`) in the message content dynamically. **Example response** ```plaintext { "code": "SPECIALOFFERFORUSER12345" } ``` #### Step 2. Create a preset with Сonnected content in Pushwoosh 1. In the [Push](https://docs.pushwoosh.com/product/content/push-presets/) or [Email content editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/), insert the Connected Content syntax into the message field. **Example** ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }} :save result %} ``` **Syntax breakdown** | | | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connected_content` | Fetches JSON data from the specified backend URL. | | `http://your-backend-url.com` | The backend endpoint that returns required data in JSON format. | | `userId={{ ${userid} }}` | A dynamic query parameter that passes the user ID to the backend. Inside `connected_content`, wrap a variable in `${}`. In the message text, write `{{userid}}` without it. | | `:save result` | Stores the fetched JSON response in the result variable for use in Liquid templates | ![Insert the Connected Content syntax](https://docs.pushwoosh.com/connectedcontent.webp) **Authentication (optional)** If your backend service requires authentication, you can include an API key or token in the Connected Content request to ensure secure access. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }}&auth=YOUR_API_KEY :save result %} ``` You can also send authentication (or any other) data as HTTP headers using the optional `:headers` parameter — a JSON object of header names and values. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }} :headers {"Authorization": "Bearer YOUR_TOKEN", "X-Api-Key": "YOUR_API_KEY"} :save result %} ``` | | | | ---------------- | ------------------------------------------------------------------------------------------ | | `:headers {...}` | A JSON object of HTTP headers sent with the request, e.g. `Authorization: Bearer `. | Static values only `${}` personalization variables work only inside the URL. Values inside `:headers` are static and are not interpolated. Keep credentials out of the URL Prefer `:headers` over a URL query parameter (e.g. `&auth=YOUR_API_KEY`) for API keys and tokens: if the fetch fails, the request URL is written to logs, exposing a key placed there. Values inside `:headers` are never logged. Connected content also requires Liquid content templates to be enabled for your account. If they aren’t, the whole `connected_content` tag — including any key inside it — is sent to every recipient as literal text instead of being rendered. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you’re unsure whether your account has this enabled. **Using tags in Connected content** To include a tag value, add it to the URL as a query parameter in the same `{{ ${tag_name} }}` form. Written as `{{ tag_name }}` inside the `connected_content` tag, the value is sent empty. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }}&lang={{ ${Language} }} :save result %} ``` 2. Next, add the message text incorporating the **retrieved data**, like this: ```plaintext Hey, {{userid}}, grab your personal promo code - {{result.code}} ``` ![Add the message text with the retrieved data](https://docs.pushwoosh.com/connectedcontent-1.webp) 3. After finalizing the message content and configuring the preset settings, save it for reuse in campaigns. [](https://docs.pushwoosh.com/connectedcontent-2.webm "Send a message with connected content") #### Step 3. Send a message using the configured preset Send a message with this preset using the [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#how-to-send-a-push-notification-using-the-one-time-push-form) or [email form](https://docs.pushwoosh.com/product/messaging-channels/emails/send-email/#how-to-send-a-one-time-email) or [customer journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Important If the service returns a status other than HTTP 200 OK, the email or push notification will not be sent. This ensures that your communication only goes out if the necessary data is successfully retrieved. --- # Multilanguage **URL:** https://docs.pushwoosh.com/developer/guides/personalization/multi-language/ Send localized push notifications using the Pushwoosh API. Add multiple language versions in one payload to reach users in their preferred language. --- Pushwoosh supports multi-language messaging within a single push notification. This feature allows you to include multiple language versions of your message (e.g., three, five, or more), making it easier to localize content for users across different regions. It’s an essential tool for apps with multi-national audiences, ensuring that messages are delivered in the user’s preferred language without the need for multiple campaigns. This feature is available in the Pushwoosh API as a parameter of the [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) request: ```plaintext "content": { "en": "HelloWorld!", "es": "¡HolaMundo!" }, ``` --- # Developer Knowledge hub **URL:** https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/ Learn key developer-focused Pushwoosh concepts, from device identifiers to device types and User IDs. --- The Pushwoosh developer Knowledge Hub provides essential technical information about device identification and user management. This section covers the following topics: [Device identifiers ](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/) [Learn how to retrieve HWID and push tokens across platforms](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/) [Types of registered devices ](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/) [Understand Pushwoosh device types and notification delivery logic](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/) [Users (User IDs) ](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/) [Learn how to manage User IDs and link devices](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/) Tip For omnichannel campaigns, data collection practices, and security certifications and compliance, see the [Product Knowledge Hub](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/). --- # Understanding device identifiers in Pushwoosh **URL:** https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/ Learn how to retrieve HWID and push tokens across platforms using Pushwoosh SDK logs, API requests, and browser dev tools for accurate device targeting. --- There are two ways to identify a particular device registered with Pushwoosh – a hardware ID (HWID) and a push token. ## HWID HWID (or Hardware ID) is a key identifier assigned to the device by Pushwoosh SDK. ### Android HWID For Android devices, HWID is a random set of letters and digits generated by the first app with the Pushwoosh SDK installed on the device. It’s later shared with other apps that contain Pushwoosh SDK so that all such apps will have the same HWID as a result. However, if a user deletes all the apps with Pushwoosh SDK and then re-installs some, a new HWID will be generated once again. Android HWID example: `5dcaed99-e1e0-44dc-bde2-b96188b99e9e` ### iOS HWID For iOS devices, IDFV ([Identifier for Vendor](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)) is being used as a HWID of the device. It won’t change as long as there is at least one app from the same vendor installed on the device. iOS HWID example (upper case only!): `CBAF8ED1-17FB-49A3-73BD-DC79B63AEF93` ## Push token Push token (device token) is a unique key for the app-device combination issued by the Apple or Google push notification gateways. It allows gateways and push notification providers to route messages and ensure the notification is delivered to the unique app-device combination for which it is intended. ### iOS push token iOS device push tokens are strings with 64 hexadecimal symbols. iOS push token example: `03df25c845d460bcdad7802d2vf6fc1dfde97283bf75cc993eb6dca835ea2e2f` Caution Make sure that the iOS push tokens you use when targeting specific devices in your API requests are in **lower case**. ### Android push token Android device push tokens can differ in length (usually below 255 characters) and typically start with APA91b…; however, it might also have a colon-separated prefix. Android push token example: `APA91bFoi3lMMre9G3XzR1LrF4ZT82_15MsMdEICogXSLB8-MrdkRuRQFwNI5u8Dh0cI90ABD3BOKnxkEla8cGdisbDHl5cVIkZah5QUhSAxzx4Roa7b4xy9tvx9iNSYw-eXBYYd8k1XKf8Q_Qq1X9-x-U-Y79vdPq` Caution Using raw device tokens for targeting specific devices is not the most reliable way because FCM push tokens tend to change from time to time, and it’s hard to tell how often it occurs. ## Collecting logs, HWID, and push token from a device ### iOS First, if you have access to your app’s source code, make sure that `Pushwoosh_LOG_LEVEL` in your app is set to `VERBOSE`. To do this, check the `Pushwoosh_LOG_LEVEL` string key in your `Info.plist` file. If not, skip to the next step. Here are the possible values for this key: * `NONE` - No logs from SDK * `ERROR` - Only display errors in the console * `WARNING` - Also, display warnings * `INFO` - Add informational messages * `DEBUG` - Add debug info * `VERBOSE` - All logs from SDK The `VERBOSE` key value allows the Pushwoosh SDK to display important information regarding registration actions. 2. Further, connect the device to your Mac and launch Xcode. Go to Window->Device and Simulators. ![Xcode Devices and Simulators window showing connected iOS device](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-1.webp) 3. In the window opened, select your connected device and click the up-triangle at the bottom left to see the device console: ![Xcode device console view showing device logs with up-triangle button highlighted](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-2.webp) Starting from Xcode 9.3 version, the process of obtaining the device console log is slightly changed. All you need to do is to go to your Launchpad and start the “Console” application. Select your device in the left sidebar. That’s it! ![macOS Console application showing connected iOS device selected in left sidebar with device logs displayed](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-3.webp) 4. Next, start your app on the device and wait for the dialog window. Allow push notifications for your app. 5. After these steps, select the entire device log from the console window and copy it to any text editor. [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/) Pushwoosh API request is what you are looking for: ![Device console log showing registerDevice API request with hwid and push token parameters visible](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-4.webp) If the device is registered correctly, this request must contain the following parameters, including “hwid” and “push token” values: Example ```txt x | Pushwoosh request: | Url: https://D868A-C13C1.api.pushwoosh.com/json/1.3/registerDevice | Payload: {"request":{"gateway":"production","jailbroken":0,"app_version":"1.9","application":"D868A-C13C1","sounds":["bubble.wav"],"device_type":1,"userId":"1457691684303547","language":"en","hwid":"FBCA07FE-05EE-4770-B4BA-AB6737BC1A98","package":"com.pushwoosh.pushon","timezone":"25200","push_token":"ae784019d1b8c8e38a1ea06f0800c17a4fa986d6e2d346fc79373e92439ff8f7","os_version":"11.2.2","v":"5.5.1","device_model":"iPhone7,1"}} | Status: "200 no error" | Response: {"status_code":200,"status_message":"OK","response":{"iosCategories":[{"categoryId":0,"buttons":[{"id":"33389","label":"test","type":"0","startApplication":0},{"id":"1233","label":"ajhdkj","type":"1","startApplication":0}]}]}} x ``` The notice with a push token of your device should present as well: ```plaintext : [PW] [I] -[PWPushNotificationsManagerCommon] Registered for push notifications: ae784019d1b8c8e38a1ea06f0800c17a4fa986d6e2d346fc79373e92439ff8f7 ``` ### Android 1. If you have access to the app’s source code, make sure that the Pushwoosh log level is set to `NOISE`. To do so, add `com.pushwoosh.log_level` meta data with the value `NOISE` to the `AndroidManifest.xml`. 2. Next, run Android Studio on your Mac/PC and go to Tools -> Android -> Android Device Monitor. ![Android Studio Android Device Monitor window showing connected Android device](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-5.webp) 3. Connect your device to the Mac/PC and launch the app. Wait about 30 seconds, select the entire device log from the console window and copy it to any text editor. ![Android Device Monitor console log window showing device logs with registerDevice request visible](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-6.webp) Please note that in the latest versions of Android Studio, you can also use the Logcat tool to obtain the device console log. Run Android Studio and go to View -> Tool Windows -> Logcat: ![Android Studio interface showing Logcat tool window accessed from View menu](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-7.webp) After that, you can locate the device log in the Logcat window: ![Android Studio Logcat window displaying device logs with registerDevice API request visible](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-8.webp) Here’s the example below, how the Pushwoosh /registerDevice API request looks like within the device console log: ![Android device console log showing registerDevice API request with hwid and push token parameters highlighted](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-9.webp) Find the “hwid” and “push token” params value in the request: Example ```txt [RequestManager] x | Pushwoosh request: | Url: https://D868A-C13C1.api.pushwoosh.com/json/1.3/registerDevice | Payload: {"request":{"application":"D868A-C13C1","hwid":"fc79efabee7649ec","v":"5.5.2","device_type":3,"userId":"fc79efabee7649ec","device_name":"Phone","language":"en","timezone":25200,"android_package":"com.pushwoosh.pushon","jailbroken":0,"device_model":"Sony F8332","os_version":"8.0.0","idfa":"d43ad490-4e6a-48a8-b8b9-f20b76fbf07b","app_version":"1.5","push_token":"dwMAeCRabUE:APA91bGSk8SqCSycxfltrWCV9KtXGo48-8qvNKfzHn758PW7lLXSi_Wd7y3gqw-lbexsjLwsaXStpELTJUAQVZ3ybNqAUmfw-we-62rTjpNbvBa4oPYTwaPCr3s4M05xNv8jGau-MeA_","sounds":"[]"}} | Response: {"status_code":200,"status_message":"OK","response":null} x [RequestManager] registerDevice response success ``` Push token is included in the device registration notice: ```plaintext I/Pushwoosh(12733): [DeviceRegistrar] Registered for pushes: dwMAeCRabUE:APA91bGSk8SqCSycxfltrWCV9KtXGo48-8qvNKfzHn758PW7lLXSi_Wd7y3gqw-lbexsjLwsaXStpELTJUAQVZ3ybNqAUmfw-we-62rTjpNbvBa4oPYTwaPCr3s4M05xNv8jGau-MeA_ 03-30 14:45:10.539: D/PushOn(12733): Registered for push notifications ``` ### Chrome (desktop) 1. Go to your website. 2. Right-click anywhere on the page and press “Inspect”. ![Chrome browser Developer Tools opened with Inspect Element context menu option visible](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-10.webp) 3. In the window opened, go to the “Application” tab. 4. Then, on the left sidebar, choose IndexedDB -> Pushwoosh_SDK_Store -> keyValue. Here, find the HWID and push token params. ![Chrome Developer Tools Application tab showing IndexedDB with Pushwoosh SDK Store and keyValue containing HWID and push token](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-11.webp) ### Chrome (mobile) 1. Connect your mobile device to the computer. 2. Go to chrome://inspect/#devices on your desktop Chrome. 3. Open your website on your mobile Chrome. 4. Once the website is opened on your mobile Chrome, you will see the corresponding page within the desktop Chrome page. Please take a look at the screenshot below: ![Chrome remote debugging page showing connected mobile device with Inspect button highlighted](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-12.webp) 5. Press the “Inspect” button highlighted in the screenshot above. 6. The following window will appear on your desktop Chrome. There, within the “Application” tab, open IndexedDB -> PUSHWOOSH_SDK_STORE. After this, click keyValue and open the API_PARAMS section: ![Chrome Developer Tools Application tab showing IndexedDB PUSHWOOSH SDK STORE with API_PARAMS section containing hwid and push token](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-13.webp) In the API_PARAMS section, you will find the “hwid” and “push token” parameters with their values. ### Safari 1. Go to your website. 2. Right-click anywhere on the page and select Inspect Element. 3. Go to the Storage tab -> Indexed Databases -> PUSHWOOSH_SDK_STORE -> keyValue. 4. Expand the API_PARAMS and find the “hwid” and “push token” values here. ![Safari Developer Tools Storage tab showing Indexed Databases with PUSHWOOSH SDK STORE keyValue containing API_PARAMS with hwid and push token](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-device-identifiers-14.webp) --- # Device types explained **URL:** https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/ Understand Pushwoosh device types—Total, Push Enabled, and Push Alerts Enabled—to manage registration, tokens, and notification delivery logic in your app. --- There are three types of records about devices registered with Pushwoosh: * [Total Devices](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/#total-devices) * [Push Enabled devices](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/#push-enabled-devices) * [Push Alerts Enabled devices](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) ## Total Devices **Total Devices** number is indicated in your Pushwoosh account — the right-hand number on the top left of the Control Panel. **Total Devices** include **all devices that were registered** with Pushwoosh and were **active within the last 90 days**. They may or may not have push tokens. Devices with no push tokens are reachable with In-App Messages. Note Push token is a unique key that identifies an app on a specific device; it is issued by cloud gateways and used to deliver push notifications to the device. The device is registered once a user opens the app with the Pushwoosh SDK for the first time after installation. Once the app is launched, the SDK will make several calls to our backend, such as `/applicationOpen` and `/getInApps`. For example: ```plaintext Pushwoosh request: Url: https://api.pushwoosh.com/json/1.3/applicationOpen Payload: {"request": {"jailbroken": 0, "notificationTypes": 7, "app_version": "1.11.2","application": "66716-7F06D","device_type": 1, "userId": "76051F70-....-46B5-B4BA-F3FC94BD3534", "language": "en", "hwid": "76051F70-....-46B5-B4BA-F3FC94BD3534","package": "com.pushwoosh.pushon", "os_version": "12.1.4","v": "5.9.1","device_model": "iPhone10,1"}} ``` This will record a device with its hwid (hardware id) to the Pushwoosh database and make it eligible for receiving In-App Messages, and the app open stats will be increased by one. Once the device is recorded to a database, you can start submitting Tags for that device and assign a User ID, if necessary. At this point, the push token does not exist yet, and this device will fall under the Total Devices category. If a device is not active for more than 90 days, e.g., there are no `/applicationOpen` requests from our SDK for that device, no Events triggered, and that device does not have a push token, it will be erased from the database. This period of inactivity is configurable; to reset the inactivity period, contact your Customer Success Manager or our Customer Support team. ## Push Enabled devices Push Enabled are devices with push tokens associated. That means you can send push notifications to such devices, and cloud gateways will deliver your pushes. Push Enabled devices number is indicated in your Pushwoosh account — the left-hand number on the top left of the Control Panel. As soon as the user agrees to receive push notifications, the push token is issued by cloud gateways, such as APNS and FCM, and our SDK sends an API request to [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) endpoint of Pushwoosh with this token: ```plaintext Pushwoosh request: Url: https://api.pushwoosh.com/json/1.3/registerDevice Payload: {"request": {"gateway": "production", "jailbroken": 0, "notificationTypes": 7, "app_version": "1.11.2", "application": "66716-7F06D", "sounds": ["bubble.wav"], "device_type": 1, "userId": "76051F70-....-46B5-B4BA-F3FC94BD3534", "language": "en", "hwid": "76051F70-....-46B5-B4BA-F3FC94BD3534", "package": "com.pushwoosh.pushon", "timezone": "25200", "os_version": "12.1.4", "push_token": "9c943351faaa7080a1c076343521.......17d220a0c68f42125cd98bb", "v": "5.9.1", "device_model": "iPhone10,1"}} ``` Now, since we have a push token registered in our database for a device, it is considered Push Enabled, and you can start sending push notifications to that device. Note In practice, even if there is a push token associated with a device, some users may not see push notifications in case they disabled alerts manually from their device settings. Those users are reachable with Background (silent) pushes, In-App Messages, and can receive notifications to the Inbox. See [Push Alerts Enabled](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) for more info. When you’re sending a push notification, the APNS and FCM return us the status of each recipient’s push token. If any token is marked invalid or nonexistent, we will remove it from the database, and the number of Push Enabled devices will decrease accordingly. When the push token is removed from the system, the device is no longer in the Push Enabled category but remains in Total Devices. If a user opens the app after that, the device will be assigned a new push token and go back to Push Enabled devices. If there is no activity within 90 days after removing the token, the device is totally erased from the database. ## Push Alerts Enabled devices Push Alerts Enabled devices can display push notification alerts outside the app: on the lock screen, in the notification center, or as a badge on the app icon. It depends on whether a user enables or disables push notifications alerts from their device settings. Devices with alerts enabled are marked with the “True” value of the Push Alerts Enabled default tag, while the rest are marked “False.” You can check this in your Tags’ statistics. This Tag tracks how many users can actually receive push notification alerts when they have the app in the background or closed. Push Alerts Enabled tag is a default one and is collected automatically by Pushwoosh SDK. Latest versions of Pushwoosh SDK (released Fall 2018 or later) are required. Important The alerts status can only be updated in Pushwoosh if the user opened the app at least once after changing the notification preferences in device settings. In case the user allows push notification alerts but doesn’t open the app, we won’t recognize such user as a Push Alerts Enabled one; that’s why it’s so important that you target all devices with tokens to ensure maximum reach. Comparing Push Enabled and Push Alerts Enabled devices gives you a better picture of your opt-in rates and can help to choose the right communication channel to engage with the broader audience. Building a segment of devices with disabled alerts allows you to reach them via alternative channels and motivate them to re-enable the notification alerts. To see why a device can or can’t show alerts, for example whether the user was never asked or declined, use the [Push Permission Status](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) tag. --- # Users (User IDs) **URL:** https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/ Learn how to manage User IDs in Pushwoosh, link devices, set user-specific tags, and use API methods to control user data and deliver personalized messages. --- A User ID is a unique identifier that is assigned to each user to maintain consistent user data across all their devices. This allows you to provide your audience with a seamless user experience across their devices. ## How are users and devices related? Each user is assigned a unique identifier called User ID. User IDs can be linked to multiple devices, allowing businesses to stay connected with users even when they switch devices. This ensures that targeted messages reach the intended user on whichever device they are using. A user (User ID) is not a standalone entity; it relies on the presence of associated devices. If the last associated device is removed or linked to another user, the user will be automatically deleted from the system. A user (User ID) also functions as an entity under which several devices can be grouped, each with its own unique Hardware ID (HWID). ![Diagram showing relationship between User ID and multiple devices, each with unique Hardware ID](https://docs.pushwoosh.com/user-ids-users-userids-1.webp) ### Device limit per user Each user can have a maximum of 20 devices linked to their User ID. When a 21st device is registered, the system automatically removes one of the existing devices to make room. **How the device for removal is selected** 1. If all linked devices are email-only, a random email device is removed. 2. Otherwise, the system finds the oldest non-email device based on the last app open time and removes it. 3. If there’s no activity data to compare (e.g., devices were added via API without actual app usage), a random non-email device is removed. This ensures that your most recently active devices stay linked to your account while older, inactive devices are automatically cleaned up. ## Types of users There are two types of users: anonymous and known users. An anonymous user has their User ID equal to the device HWID, which can be replaced with the actual User ID later on. A known user has a distinct User ID. You can specify a User ID when registering a new device through the [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) API call. ## Obtaining and managing User IDs Pushwoosh provides several ways to access and manage Users: ### Creating users By default, when an app with the integrated Pushwoosh SDK is launched for the first time, it sets a device HWID as a User ID. You can call the [`setUserID`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) method from the Pushwoosh SDK on login to set any required value to associate a device with a particular user. When a user logs out, you can reset this value to a default one with another [`setUserID`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) call, e.g. to an initial HWID value. ### Importing users You can easily import User IDs from a CSV file for a specific user segment. Please find our detailed instructions in the [Segments guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import). ### Exporting users To export User IDs, you can utilize the [`/exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment) API request, which provides the option to export data in CSV or JSON format. ### Accessing User IDs from device logs User IDs can also be directly accessed from device logs. For step-by-step instructions, please refer to the article [How to Obtain Console Logs and Device Tokens](https://help.pushwoosh.com/hc/en-us/articles/360000292586-How-to-obtain-console-logs-and-device-token?source=search&auth_token=eyJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50X2lkIjoyOTA5MjksInVzZXJfaWQiOjEyNDkzMTI5MDkwMzMzLCJ0aWNrZXRfaWQiOjc4MzMyLCJjaGFubmVsX2lkIjo2MywidHlwZSI6IlNFQVJDSCIsImV4cCI6MTY5MzExNDk5Mn0.SpeS-IJ_j3mB6Elf2ObMjev2MZSmDOLu4DYQdEcZvS8). ## User and device tags Just like devices, users (User IDs) can have their own sets of [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) that will serve as labels to categorize and identify users or devices based on their characteristics. ### How to create user-specific tags When you set a user-specific tag, it automatically applies to both users and all their associated devices. This means the tag will be linked to the user’s profile and will be reflected on all their devices. To create a user-specific tag from the Pushwoosh Control Panel, check the **User-specific** checkbox when adding a new tag. If you don’t mark the tag as user-specific, it will be associated with the device only. ![Pushwoosh Control Panel tag creation form showing User-specific checkbox option for creating user-specific tags](https://docs.pushwoosh.com/user-ids-users-userids-2.webp) ## Managing user transitions Within Pushwoosh, managing user transitions involves two primary scenarios: ### Transition from Anonymous to Known user When an anonymous user becomes a known user, the device they are using becomes associated with their new user profile. During this transition, all device attributes remain intact. However, it’s important to note that all user-specific attributes are reset to known user values or default if it does not exist. For instance, let’s consider a scenario where a guest user has been using a fitness tracking application with default settings. When they create an account, their device data remains unchanged. Still, any user-specific details, like their name, email, and customized goals, will be reset to known user values. ### Transition from one Known user to another If the User associated with a device is changed using **setUserID** from Pushwoosh SDK, the device is associated with the new user profile. > Learn more about setting User IDs for [Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#how-do-i-set-a-userid) and [iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-faq/#q-how-can-i-set-a-user-id-in-pushwoosh). You can also use the [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser) API call. Just like in the previous scenario, the device attributes remain the same, ensuring a smooth transition. However, the user-specific tags will now be assigned to the values of the new user. This is to ensure that the user’s tags reflect the new user’s profile accurately. As an example, consider a scenario in a video streaming service. If a registered user logs out and a different registered user logs in on the same device, the device settings, such as preferred language and video quality settings, are retained. Yet, any user-specific information, such as viewing history and recommendations, will be changed for the new user. ## API methods for managing users Pushwoosh provides several API methods to manage users within your application. Understanding these methods is crucial for effective user data management and targeting. Here are the four key methods that change the user’s status or attributes: 1. [`/applicationOpen`](https://docs.pushwoosh.com/developer/api-reference/device-api#applicationopen) * Creates a device without a push token and anonymous user if they do not exist. * Sets device-specific tags on the device. * Does not handle user-related changes. 2. [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) * Creates a device with a push token and user if they do not exist. * Associates a device with the user from the API request (anonymous user otherwise) * Assigns all specified tags to both the device and its associated user. Caution To safeguard against potential race conditions (where multiple operations attempt to modify shared data simultaneously), refrain from using [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) concurrently with [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser). 3. [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser) * Creates a device without a push token and user if it does not exist. * Transfers a device between users while retaining device tags. 4. [`/setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) * Creates a device without a push token and anonymous user if it does not exist. * Assigns device tags to the device. * Assigns user tags to the user provided in the request (if such a user exists). * This method does not transfer the device, and as a result, it cannot create a new known user (you can use [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#registeruser) method to create a new known user). ## If a user is associated with multiple devices, which one will receive the message during a Customer Journey? When a user has multiple devices associated with their account, how messages are delivered across them depends on the journey type: ### Audience-based entry and webhook-based entry journeys By default, in these journeys, messages are sent to the most recently active device associated with the user. This ensures the message reaches the user who is most likely to engage with it. ### Trigger-based entry journeys By default, the message is sent to the device where the triggering action was last fired. This maintains context and relevance by delivering the message on the device where the relevant user activity occurred. ### All devices delivery However, you can choose to override these default settings and send the message to all devices associated with the User ID simultaneously. For this, in the message settings, scroll down to **Options**, and toggle on **Send to all user devices**. ![Customer Journey message settings showing Send to all user devices toggle option in Options section](https://docs.pushwoosh.com/user-ids-users-userids-3.webp) This option might be useful for broadcasts or urgent notifications that need to reach the user regardless of which device they’re currently using. --- # Android SDK Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/ Explore the official Pushwoosh Android SDK documentation. Find integration guides (Firebase/HMS), API reference, examples & support to add push notifications to your Android app --- ## Setting up Android SDK Choose the integration method that best suits your target platforms: * [AI-Assisted Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/ai-integration/) - use AI coding assistants to integrate and configure the Pushwoosh Android SDK Or doing by yourself: ### Firebase Cloud Messaging (FCM) Firebase Cloud Messaging is Google’s recommended push notification service for Android applications. To integrate FCM with Pushwoosh: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/quick-start/) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/) - detailed walkthrough with advanced options ### Huawei Mobile Services (HMS) For Huawei devices without Google Play Services, integrate using Huawei Push Kit: [Huawei Integration Guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/huawei-integration/) ### Amazon Device Messaging (ADM) For Amazon devices, use our Amazon Device Messaging integration: [Amazon Integration Guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/amazon/) ## Technical Documentation [Android SDK API Reference](https://pushwoosh.github.io/pushwoosh-android-sdk/) — complete technical documentation covering all public classes, methods, and properties ## Live Updates [Send Live Updates on Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/live-updates/) - render ongoing, progress-style notifications for delivery, ride-hailing, and order tracking on Android 16+ ## Feature flags [Feature flags](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/) - read the flags set in the Control Panel and switch features in your app per user ## SDK Customization [Learn how to customize Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/) ## Troubleshooting and FAQ [Explore common issues and solutions](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/) ## Support and community Have questions or problems with the Pushwoosh SDK? [Reach out to our support team!](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community/) ## References [Android SDK API Reference ](https://pushwoosh.github.io/pushwoosh-android-sdk/)Complete technical documentation covering all public classes, methods, and properties. [Android SDK Source Code ](https://github.com/Pushwoosh/pushwoosh-android-sdk)Browse the open-source Android SDK repository on GitHub. [Android Demo Application ](https://github.com/Pushwoosh/pushwoosh-android-sample)Sample app demonstrating Pushwoosh Android SDK integration. --- # AI-Assisted Integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/ai-integration/ Integrate Pushwoosh Android SDK using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP --- Use AI coding assistants to integrate and configure the Pushwoosh Android SDK in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date SDK documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh Android SDK docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh Android SDK library ID on Context7: **`/pushwoosh/pushwoosh-android-sdk`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the SDK. Below are ready-to-use prompts for common tasks. ### Basic SDK integration ```text Integrate Pushwoosh Android SDK into my Android project with Firebase Cloud Messaging (FCM). Requirements: - Add gradle dependencies (pushwoosh, pushwoosh-firebase) - Configure AndroidManifest.xml with Pushwoosh App ID: YOUR_APP_ID - Register for push notifications in MainActivity Use Context7 MCP to fetch Pushwoosh Android SDK documentation. ``` ### Push notification handling ```text Show me how to handle push notification callbacks (receive, open) with Pushwoosh SDK in Android. I want to add analytics tracking for these events. Use Context7 MCP to fetch Pushwoosh Android SDK documentation for NotificationServiceExtension. ``` ### User segmentation with tags ```text Show me how to use Pushwoosh tags for user segmentation in Android. Create example helper class with methods for setting and getting tags. Use Context7 MCP to fetch Pushwoosh Android SDK documentation for setTags and getTags. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh Android SDK documentation”** at the end of your prompt tells the AI assistant to pull the latest SDK docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh Android SDK, it receives: * **Installation instructions** — Gradle dependencies and manifest configuration * **API reference** — public classes, methods, and interfaces * **Code examples** — Java and Kotlin snippets for common use cases * **Module documentation** — guides for each SDK module (Firebase, Huawei, Geozones, etc.) * **Configuration details** — AndroidManifest.xml settings, Firebase setup, and permissions --- # Amazon integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/amazon/ Learn how to integrate Pushwoosh SDK into your Amazon application using Amazon Device Messaging (ADM) for push notifications --- [**Download SDK**](https://github.com/Pushwoosh/pushwoosh-android-sdk)\ [**Sample Project**](https://github.com/Pushwoosh/pushwoosh-android-sample)\ [**SDK API Docs**](https://github.com/Pushwoosh/pushwoosh-android-sdk/tree/master/Documentation) To integrate Pushwoosh push notifications into your Amazon application, follow these steps: **1.** Check out the Android integration [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk) **2.** Get an API KEY from Amazon: Caution For a pre-release or “debug” version of your app, create an API Key and store it as the only data in a file named `api_key.txt`. Place the file in the assets folder of your project. Note: ADM cannot recognize your API Key if it contains any spaces or line breaks. For a release or “production” version of your app, you may not need to create an API Key. You will need an additional API Key for the release version of your app if you sign it using your own certificate. Otherwise, if you allow Amazon to sign your app on your behalf, you do not need to create an additional API Key. **3.** Add `com.pushwoosh:pushwoosh-amazon` library to your application build.gradle ```txt implementation 'com.pushwoosh:pushwoosh-amazon:+' ``` ## Troubleshooting If a push isn’t delivered to Amazon devices, see [Amazon errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/amazon-errors/) for the error codes and how to fix each one. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # FAQ on Pushwoosh Android SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/ Find answers to frequently asked questions about Pushwoosh Android SDK integration, push tokens, permissions, and troubleshooting --- * [What does Android push token and HWID look like?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#what-does-android-push-token-and-hwid-look-like) * [How can I obtain my Android device push token?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#how-can-i-obtain-my-android-device-push-token) * [What permissions are necessary and what are optional?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#what-permissions-are-necessary-and-what-are-optional) * [How do I set a UserID?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#how-do-i-set-a-userid) * [How accurate is the total number of Android subscribers?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#how-accurate-is-the-total-number-of-android-subscribers) * [Can I use HTML tags in pushes sent to Android?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#can-i-use-html-tags-in-pushes-sent-to-android) * [How to set a notification icon in Android Lollipop (and later versions)?](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#how-to-set-a-notification-icon-in-android-lollipop-and-later-versions) * [Using Pushwoosh SDK with other FCM services](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#using-pushwoosh-sdk-with-other-fcm-services) * [Using Pushwoosh with LeakCanary or AppMetrica libraries](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#using-pushwoosh-with-leakcanary-or-appmetrica-libraries) * [Deleting a received notification from a device](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#deleting-particular-received-notifications-from-a-device) ### What does Android push token and HWID look like? Android device push tokens differ in length (less than 255 characters) and start with **APA91b**, for example: `APA91bFoi3lMMre9G3XzR1LrF4ZT82_15MsMdEICogXSLB8-MrdkRuRQFwNI5u8Dh0cI90ABD3BOKnxkEla8cGdisbDHl5cVIkZah5QUhSAxzx4Roa7b4xy9tvx9iNSYw-eXBYYd8k1XKf8Q_Qq1X9-x-U-Y79vdPq` Tokens often contain a colon-separated prefix before **APA91b**, for example: `eQnyCE6ULAQ:APA91bGrh4ya3b_owo9tshZNVAGhZdGMGb3sA5HbM...` Pushwoosh uses UUID as HWIDs, i.e. randomly generated strings of 32 alphanumerical characters: `123e4567-e89b-12d3-a456-426655440000` ### How can I obtain my Android device push token? You can obtain your Android device push token in the console log. Use the logcat tool in [Android Studio](https://developer.android.com/studio). Open **monitor.bat** in `%USERPROFILE%\AppData\Local\Android\sdk\tools\monitor.bat`, connect your device to PC and allow USB debugging in Android settings. Run your application on the device. Locate `/registerDevice`, find the push token for your device to use in Test Devices later on. ![Screenshot showing logcat output with push token registration information displayed in Android Studio](https://docs.pushwoosh.com/android-push-notifications-android-faq-1.webp) ### What permissions are necessary and what are optional? When installed on an Android device, the application will ask for the following permissions in connection with Pushwoosh SDK: ```java ``` Our SDK doesn’t ask for permission to access images, device contacts, etc. ### How do I set a UserID? You can set a [User identifier (UserID)](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/), which could be a Facebook ID, username, email, or any other unique user ID. This allows you to match data and events across multiple devices associated with the same user. To set the UserID, call the **setUserId** method. **Example** ```plaintext Pushwoosh.getInstance().setUserId("testUser"); ``` ### How accurate is the total number of Android subscribers? Pushwoosh clears unsubscribed Android devices from the database upon receiving the [NotRegistered](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/android-chrome-firefox-errors/) response from FCM, which can be returned after the second attempt to reach a specific device. This means that you have to send 2 pushes to an unsubscribed device to have it removed from the database. The following is the most common scenario described in the [FCM documentation](https://firebase.google.com/docs/cloud-messaging/): 1. Your subscriber uninstalls the app. 2. Pushwoosh sends a message to FCM server. 3. The FCM server sends the message to your user’s device. 4. The FCM client on the device receives the message and detects that your application has been uninstalled; the detection details depend on the platform on which the app is running. 5. The FCM client on the device informs the FCM server that the app was uninstalled. 6. The FCM server marks the registration ID for deletion. 7. Pushwoosh sends another message to FCM. 8. The FCM returns a NotRegistered message. 9. Pushwoosh removes the push token from your userbase. It might take a while for registration ID to be completely removed from FCM. Thus it is possible a message sent in step 7 above gets a valid message ID as response, even though the message will not be delivered to the client app. ### Can I use HTML tags in pushes sent to Android? Yes, in Android you may use the following HTML tags in order to modify the appearance of a push: ```txt Hello world! Hello hi hey ``` Place these HTML tags in the **Message** input field, and use them in the API request as well. Some Android devices may fail to process these HTML tags properly, but most devices display formatting correctly. ### How to set a notification icon in Android Lollipop (and later versions)? In Android Lollipop icons were changed to be white only. Therefore, if you select **targetSdkVersion >= 21** in your **AndroidManifest.xml** file, Android will only use alpha-channel of the icon.\ See more on the behavior in [Android documentation](https://developer.android.com/docs). The system ignores all non-alpha channels in action icons and in the main notification icon. Assume these icons will be alpha-only. The system draws notification icons in white and action icons in dark gray. *This is beyond Pushwoosh SDK control*. **1.** Create the notification icon according to the [Android guidelines](https://material.io/design/platform-guidance/android-notifications.html#style). As per [documentation](https://developer.android.com/about/versions/lollipop/android-5.0-changes), the system will ignore all the colors. **2.** Name the icon as `pw_notification.png` and put it in **res/drawable** folder. Pushwoosh SDK will use this icon as default for notifications. **3.** Alternatively, you can use Remote API and set the `"android_icon"` parameter value to the icon image (without file extension). ### Using Pushwoosh SDK with other FCM services You can use Pushwoosh alongside other SDKs that use FCM for push messaging. To do that, create a router service to distribute events between the services. First, add the **pushwoosh-firebase** dependency alongside the main Pushwoosh module: build.gradle ```groovy implementation 'com.pushwoosh:pushwoosh-firebase:6.0.3' ``` Create the routing class: * FirebaseMessagingRouterService.kt ```kotlin import com.pushwoosh.firebase.PushwooshFcmHelper; class FirebaseMessagingRouterService : FirebaseMessagingService() { override fun onNewToken(token: String?) { super.onNewToken(token) PushwooshFcmHelper.onTokenRefresh(token) sendTokenToAnotherService(token) } override fun onMessageReceived(remoteMessage: RemoteMessage) { if (PushwooshFcmHelper.isPushwooshMessage(remoteMessage)) { //this is a Pushwoosh push, SDK will handle it automatically PushwooshFcmHelper.onMessageReceived(this, remoteMessage) } else { //this is not a Pushwoosh push, you should handle it by yourself dispatchNonPushwooshMessage(remoteMessage); } } private fun dispatchNonPushwooshMessage(remoteMessage: RemoteMessage) { // Implement your push handling logics here } } ``` * FirebaseMessagingRouterService.java ```java import com.pushwoosh.firebase.PushwooshFcmHelper; public class FirebaseMessagingRouterService extends FirebaseMessagingService { @Override public void onNewToken(String token) { super.onNewToken(token); PushwooshFcmHelper.onTokenRefresh(token); sendTokenToAnotherService(token); } @Override public void onMessageReceived(RemoteMessage remoteMessage) { if (PushwooshFcmHelper.isPushwooshMessage(remoteMessage)) { //this is a Pushwoosh push, SDK will handle it automatically PushwooshFcmHelper.onMessageReceived(this, remoteMessage); } else { //this is not a Pushwoosh push, you should handle it by yourself dispatchNonPushwooshMessage(remoteMessage); } } private void dispatchNonPushwooshMessage(RemoteMessage remoteMessage) { // Implement your push handling logics here } } ``` Register the routers in your AndroidManifest.xml: AndroidManifest.xml ```txt ``` ### Using Pushwoosh with LeakCanary or AppMetrica libraries When you integrate analytics tools such as LeakCanary, AppMetrica or others, these libraries start a new process, creating new instance of the app. Since you can’t listen for the push notifications in another process, this results in `java.lang.NullPointerException` being thrown. If you call `registerForPushNotifications` inside `Application.onCreate()`, you should check if you are in the application’s main process. Use the following code to perform this check: ```java List runningAppProcesses = ((ActivityManager) getSystemService(Context.ACTIVITY_SERVICE)).getRunningAppProcesses(); if (runningAppProcesses != null && runningAppProcesses.size() != 0) { for (ActivityManager.RunningAppProcessInfo runningAppProcessInfo : runningAppProcesses) { boolean isCurrentProcess = runningAppProcessInfo.pid == android.os.Process.myPid(); boolean isMainProcessName = getPackageName().equals(runningAppProcessInfo.processName); if (isCurrentProcess && isMainProcessName) { Pushwoosh.getInstance().registerForPushNotifications(...); break; } } } ``` ### Deleting particular received notifications from a device locally If you want to remove a non-relevant push when performing a specific action inside your application, you can use the following approach: MainActivity.java ```java public class MainActivity extends AppCompatActivity { List savedPushes = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Pushwoosh.getInstance().registerForPushNotifications(); PushwooshNotificationSettings.setMultiNotificationMode(true); findViewById(R.id.clear_button).setOnClickListener( v -> { cancelPushes(); }); EventBus.subscribe(NotificationCreatedEvent.class, event -> { try { if (event.getMessage().getCustomData() == null) return; //cancel pushes only with a specific flag if (new JSONObject(event.getMessage().getCustomData()).getBoolean("cancel")) { savedPushes.add(event); } } catch (JSONException e) { } }); } private void cancelPushes() { NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE); if (manager == null) { return; } for (NotificationCreatedEvent event : savedPushes) { manager.cancel(event.getMessageTag(), event.getMessageId()); } } } ``` ## Share your feedback with us Your feedback helps us create a better experience. If you face any difficulties during the SDK integration process, share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Android modal rich media **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-modal-rich-media/ The Android Modal Rich Media feature allows developers to display HTML content inside a modal popup window within their application. This popup does not block other UI elements and can be easily dismissed using swipe gestures. The modal offers multiple customization options for appearance, behavior, animations, and more, providing a highly flexible user experience. --- Important Available from SDK 6.7.13 The Android Modal Rich Media feature allows developers to display HTML content inside a modal popup window within their application. This popup does not block other UI elements and can be easily dismissed using swipe gestures. The modal offers multiple customization options for appearance, behavior, animations, and more, providing a highly flexible user experience. ## Key features * **Non-blocking UI**: The modal does not block interaction with other UI elements. * **Customizable Swipe Gestures**: Configure swipe gestures to dismiss the modal in various directions. * **Flexible Positioning**: Choose where the modal appears on the screen (top, center, or bottom). * **Custom Animations**: Control how the modal appears and disappears with different animation types. * **Dismissable via Swipe**: Users can swipe to dismiss the modal from any direction. * **Adjustable Animation Duration**: Fine-tune the speed of modal animations. * **Status Bar Handling**: Option to show the modal beneath or above the status bar. * **Adjustable Window Width**: Option to set the modal width to full screen or wrap content. ## Usage ### Enabling modal Rich media To enable the **Modal Rich Media** feature, you need to configure it in your application’s `AndroidManifest.xml` file by adding the following metadata entry: ```xml ``` ## Default configuration The appearance and behavior of the modal can be customized by setting a default configuration through the `RichMediaManager`. This configuration allows you to specify how the modal behaves, where it appears, how it can be dismissed, and the type of animations used. ## Customization options ### Modal view position You can set the position of the modal on the screen using `setViewPosition()`. The available values for this parameter are: * `TOP` * `CENTER` (default) * `BOTTOM` ### Dismiss animation types Customize how the modal dismisses with `setDismissAnimationType()`. Available options include: * `FADE_OUT` (default) * `SLIDE_UP` * `SLIDE_DOWN` * `SLIDE_LEFT` * `SLIDE_RIGHT` * `NONE` ### Present animation types Define how the modal appears with `setPresentAnimationType()`. Available options are: * `FADE_IN` * `SLIDE_UP` * `SLIDE_RIGHT` * `DROP_DOWN` * `SLIDE_LEFT` * `NONE` (default) ### Swipe gesture configuration The modal can be dismissed with swipe gestures in different directions using `setSwipeGesture()`. Available swipe gestures include: * `UP` * `LEFT` * `RIGHT` * `DOWN` * `NONE` (default) ### Animation duration Control the animation speed by setting the `animationDuration()` in milliseconds. The duration is specified as an integer value, representing the time in milliseconds. ### Modal Window Width The modal’s width can be set using `setWindowWidth()`, which defines whether the modal will take the full width of the screen or just enough to fit the content. The possible values are: * `FULL_SCREEN` (default) * `WRAP_CONTENT` ### Status bar handling By default, the modal will not cover the status bar. To allow the modal to appear beneath the status bar, set the `setStatusBarCovered()` property to `true`. ## Example configuration Here are some examples, demonstrating various animations, including swipe gestures, slide-in appearances, fade-out dismissals, different animation durations, and varying screen widths. Below, you can also find the configurations used to achieve these effects. ### Swipe gesture example [](https://docs.pushwoosh.com/android-push-notifications-android-modal-rich-media-1.webm "Android modal with swipe right gesture to dismiss") ```plaintext RichMediaManager.setDefaultRichMediaConfig(new ModalRichmediaConfig() .setStatusBarCovered(false) .setSwipeGesture(ModalRichMediaSwipeGesture.RIGHT) .setViewPosition(ModalRichMediaViewPosition.CENTER) .setDismissAnimationType(ModalRichMediaDismissAnimationType.SLIDE_RIGHT) .setPresentAnimationType(ModalRichMediaPresentAnimationType.SLIDE_FROM_LEFT) .setAnimationDuration(800) .setWindowWidth(ModalRichMediaWindowWidth.WRAP_CONTENT)); ``` ### Drop-down animation example [](https://docs.pushwoosh.com/android-push-notifications-android-modal-rich-media-2.webm "Android modal appearing with drop-down animation and swipe up to dismiss") ```plaintext RichMediaManager.setDefaultRichMediaConfig(new ModalRichmediaConfig() .setSwipeGesture(ModalRichMediaSwipeGesture.UP) .setViewPosition(ModalRichMediaViewPosition.TOP) .setDismissAnimationType(ModalRichMediaDismissAnimationType.SLIDE_UP) .setPresentAnimationType(ModalRichMediaPresentAnimationType.DROP_DOWN) .setAnimationDuration(1000)); ``` ### Bottom Rich media example [](https://docs.pushwoosh.com/android-push-notifications-android-modal-rich-media-3.webm "Android modal displayed at bottom with fade out dismissal and slide up animation") ```plaintext RichMediaManager.setDefaultRichMediaConfig(new ModalRichmediaConfig() .setSwipeGesture(ModalRichMediaSwipeGesture.DOWN) .setViewPosition(ModalRichMediaViewPosition.BOTTOM) .setDismissAnimationType(ModalRichMediaDismissAnimationType.FADE_OUT) .setPresentAnimationType(ModalRichMediaPresentAnimationType.SLIDE_UP) .setAnimationDuration(2000)); ``` ## Conclusion The Android Modal Rich Media feature provides an easy and flexible way to present HTML content inside your app without obstructing the user experience. With its extensive customization options, including swipe gestures, animations, positioning, and window width, you can create a seamless and engaging interaction for your users. --- # Change Android app icon **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-notification-customization/change-android-app-icon/ Learn how to change the application icon dynamically based on the number of unread notifications in the notification center --- To change the application icon based on the number of notifications in the notification center, follow the steps below. 1. Add a new icon to your project in the ‘mipmap’ folder. ![Screenshot showing the mipmap folder structure in Android Studio with a new icon file added](https://docs.pushwoosh.com/android-notification-customisation-change-android-app-icon-1.webp) 2. Create a new Activity class. ```java import android.os.Bundle; import androidx.appcompat.app.AppCompatActivity; public class MainActivityAlias extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } } ``` 3. **Create a class with the logic for changing the icon.** ```java import android.content.ComponentName; import android.content.Context; import android.content.pm.PackageManager; public class MyPushReceiver { public static void changeAppIcon(Context context, int count) { PackageManager pm = context.getPackageManager(); if (count > 0) { pm.setComponentEnabledSetting( new ComponentName(context, "com.example.app.MainActivityAlias"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ); pm.setComponentEnabledSetting( new ComponentName(context, "com.example.app.MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ); } else { pm.setComponentEnabledSetting( new ComponentName(context, "com.example.app.MainActivity"), PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP ); pm.setComponentEnabledSetting( new ComponentName(context, "com.example.app.MainActivityAlias"), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP ); } } } ``` 4. **In your NotificationServiceExtension class, implement tracking for receiving/deleting push notifications.** ```java @Override public boolean onMessageReceived(final PushMessage message) { super.onMessageReceived(message); if (!message.isSilent()) { /** * In this example, a random number of notifications is simply indicated. * You need to implement the logic for counting notifications yourself. */ int notificationCount = 1; if (getApplicationContext() != null) { MyPushReceiver.changeAppIcon(getApplicationContext(), notificationCount); } } } /** * Pushwoosh callback for tracking push notification removal from Notification Center. */ @Override protected void onMessageCanceled(PushMessage message) { super.onMessageCanceled(message); if (!message.isSilent()) { /** * In this example, a random number of notifications is simply indicated. * You need to implement the logic for counting notifications yourself. */ int notificationCount = 0; if (getApplicationContext() != null) { MyPushReceiver.changeAppIcon(getApplicationContext(), notificationCount); } } } ``` 5. **Done!** ### Example of how it looks on the device [](https://docs.pushwoosh.com/android-notification-customisation-change-android-app-icon-2.webm "Animated demonstration showing the app icon changing dynamically when notifications are received or removed") --- # Compliance with Google Package Visibility policy **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/compliance-with-google-package-visibility-policy/ How to prevent issues related to Google Package Visibility policy when publishing apps with Pushwoosh SDK --- As Google has updated its Developer Program Policy, some issues with publishing apps with Pushwoosh SDK to Google Play may raise. Refer to the [Google Policy Center](https://support.google.com/googleplay/android-developer/answer/10446026) for details. To overcome or prevent those issues, please make sure to provide your app complying with the updated Package Visibility policy (effective Summer 2021) following the steps described in this guide. ## Update Pushwoosh SDK Update the Pushwoosh SDK (for Android native apps) or the corresponding plugin for any of the cross-platform frameworks to the latest version; 6.2.5 is the required minimum. Starting from version 6.2.5, any methods querying installed applications or packages from user’s device were removed from Pushwoosh SDK, particularly: * [android.content.pm](http://android.content.pm/).PackageManager.getInstalledApplications() * [android.content.pm](http://android.content.pm/).PackageManager.getInstalledPackages() ## Ensure other SDKs are compliant Not only Pushwoosh SDK may be subject to the updated policy, but any other SDK implemented in your app, so you have to verify they are compliant to resubmit the app to Google Play. Analyze your APK to ensure there is no other library using the `getInstalledApplications()` and `getInstalledPackages()` methods. 1. Launch the [Android Studio](https://developer.android.com/studio). 2. Download the .apk. 3. Open **Build**-> **Analyze APK…**, find your **.apk**, and select it. 4. You will see the **View** where you can find the **.apk** resources. Navigate to the **.dex** files: 5. Open all **.dex** files one-by-one and find the `android.content.pm.PackageManager`: 6. Look for the methods `getInstalledApplications()` and `getInstalledPackages()`. If there are any, right-click it and choose the **Find usages** option: It will indicate what SDKs use those methods. If you are using Ogury, Pollfish, Adjoe.io, ironSource, or UnityNativeShare – those libraries may be using querying packages methods. 7. Make sure to update those SDKs to versions compliant with the Google Policy or remove them from the app. 8. Repeat steps 2.4-2.7 for all .dex files in your APK. ## Remove older non-compliant versions 1. Log into your Google Play Console. 2. Select the app you want to submit to the Store. 3. Check your **Production, Open, Closed,** and **Internal testing** tracks for old APKs/AABs with outdated Pushwoosh SDK or any third-party SDK implemented that have been found out on the previous step. If you find such artifacts, remove the app bundles. For this: 1. Open a track (e.g., Internal testing). 2. Click on **Release Details**. 3. In the **App bundles and APKs** section, choose your APK/AAB and open its details (blue arrow on the right from the release). 4. Click **Explore App Bundle**. 5. Delete the app bundle by clicking the **Delete App Bundle** button. Caution Make sure the bundle is deleted, not just inactivated. 6. If a release is already published, the APK/AAB might be unavailable for deletion. In that case, update the release with the new SDK version. Repeat steps 1 - 6 for all outdated artifacts. Note Make sure to update the app in the current release before resubmitting it to Google Play. Increment the app version before resubmitting (e.g., if it’s 1.0.1, it has to become 1.0.2). ## To speed the approval up To speed up the approval made by Google, send an appeal to and attach screenshots to prove that: * Your APK does not contain any calls to these methods: [*android.content.pm*](http://android.content.pm/)*.PackageManager.getInstalledApplications()*\ [*android.content.pm*](http://android.content.pm/)*.PackageManager.getInstalledPackages()* * All your release tracks in Google Play Console have only compliant bundles. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Customize Android SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/ Customize Pushwoosh Android SDK to meet your goals and preferences --- Note Make sure you’ve integrated Pushwoosh Android SDK into your project: * [Firebase Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/quick-start/) * [Amazon Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/amazon/) ## Deep linking In your activity that will handle the deep link, add tag with the scheme, host, and pathPrefix parameters. ```txt ``` Note Deep link page name (*promotion* in the example given) goes to the **host** field, **not pathPrefix**. In the example above, the deep link will open PromoActivity. The basic implementation below displays alert with promo id value for the sake of simplicity. In your application it could definitely do something useful! ```java public class PromoActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.deep_link); setTitle("Deep link activity"); Intent intent = getIntent(); String action = intent.getAction(); Uri data = intent.getData(); if (TextUtils.equals(action, Intent.ACTION_VIEW)) { openUrl(data); } } private void openUrl(Uri uri) { String promoId = uri.getQueryParameter("id"); Toast.makeText(getApplicationContext(), promoId, Toast.LENGTH_LONG).show(); } } ``` ## In-app purchase tracking If you want to track in-app purchases in [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview), configure sending purchase information to Pushwoosh by calling this method: ```java Pushwoosh.getInstance().sendInappPurchase(@NonNull String sku, @NonNull BigDecimal price, @NonNull String currency); ``` ## Geozones push notification To use Geozone pushes, add the `com.pushwoosh:pushwoosh-location` library and call: ```java PushwooshLocation.startLocationTracking(); ``` In your **AndroidManifest.xml**, include the necessary permissions: ```xml ``` ## Using local notifications with Pushwoosh If you use Pushwoosh Local Notifications API, add RECEIVE_BOOT_COMPLETED permission to your AndroidManifest.xml: AndroidManifest.xml ```txt x ``` ## Using badge number on Android Pushwoosh supports setting badge number on the app icon shortcut for the following Android launchers:\ Sony, Samsung, LG, HTC, ASUS, ADW, APEX, NOVA, HUAWEI, ZUK, OPPO.\ To use this functionality, simply add `com.pushwoosh:pushwoosh-badge` library to your application. ## Opening custom activity If you want to start a particular activity in response to push notifications, add the following intent-filter to that activity: AndroidManifest.xml ```txt ``` ## Controlling Log Level In order to assist with debugging and integration, SDK will print all the requests to the console by default. When you are ready for the production build, add `com.pushwoosh.log_level` meta-data with value “ERROR” to the AndroidManifest.xml. This way only information about errors will go to the console. Other option could be one of the following: *NONE* - No logs from the SDK\ *ERROR* - Display only errors in the console\ *WARN* - Display also a warnings\ *INFO* - Display informational messages\ *DEBUG* - Even debug information is displayed now\ *NOISE* - Everything SDK can print and more AndroidManifest.xml ```txt ``` ## Using Proguard When using Proguard, add the following options: proguard-rules.pro ```txt -keep class com.pushwoosh.** { *; } -dontwarn com.pushwoosh.** ``` See **Google Play Services** library requirements regarding Proguard here:\ ## Customising notification open behaviour If you need to programmatically select which activity to display as a result of push notification, you can create custom [NotificationServiceExtension](https://github.com/Pushwoosh/pushwoosh-android-sdk/blob/master/Documentation/notification/NotificationServiceExtension.md) and include fully qualified class name of your NotificationServiceExtension in metadata under `com.pushwoosh.notification_service_extension` value. AndroidManifest.xml ```txt ``` YourNotificationServiceExtension.java ```java public class YourNotificationServiceExtension extends NotificationServiceExtension { @Override protected void startActivityForPushMessage(PushMessage message) { // super.startActivityForPushMessage() starts default launcher activity // or activity marked with ${applicationId}.MESSAGE action. // Simply do not call it to override this behaviour. // super.startActivityForPushMessage(message); // start your activity instead: Intent launchIntent = new Intent(getApplicationContext(), YourActivity.class); launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); // (Optional) pass notification data to Activity launchIntent.putExtra(Pushwoosh.PUSH_RECEIVE_EVENT, message.toJson().toString()); context.startActivity(launchIntent); } } ``` Note **Important** If you use proguard in production builds, make sure your custom NotificationServiceExtension is not obfuscated ( by adding `-keep class` rule), otherwise it will lead to ClassNotFoundException. ## Customizing push notifications To customize the view of push notifications, you need to create a custom Factory, you can create custom [NotificationFactory](https://pushwoosh.github.io/pushwoosh-android-sdk/pushwoosh/com.pushwoosh.notification/-notification-factory/index.html) and include fully qualified class name of your NotificationFactory in metadata under `com.pushwoosh.notification_factory` value. AndroidManifest.xml ```txt ``` YourNotificationFactory ```java public class YourNotificationFactory extends PushwooshNotificationFactory { @Override public Notification onGenerateNotification(@NonNull PushMessage pushMessage) { if (customNotification) { // TODO: generate and return custom notification } // return default Pushwoosh notification return super.onGenerateNotification(pushMessage); } } ``` ## Customizing group summary To customize the appearance of a [group summary](https://developer.android.com/training/notify-user/group#set_a_group_summary), create a custom Factory. You can create custom SummaryNotificationFactory and include fully qualified class name of your SummaryNotificationFactory in metadata under com.pushwoosh.summary_notification_factory value. AndroidManifest.xml ```java ``` YourSummaryNotificationFactory ```java public class YourSummaryNotificationFactory extends PushwooshSummaryNotificationFactory { @Override public String summaryNotificationMessage(int notificationsAmount) { // return the message you want return super.summaryNotificationMessage(notificationsAmount); } @Override public int summaryNotificationIconResId() { // return the icon resource id you want return super.summaryNotificationIconResId(); } } ``` ## Private endpoint URL Note For **Custom Plan** subscriptions only. For more details, please contact our [Sales team](https://www.pushwoosh.com/demo/?utm_source=docs&utm_medium=post&utm_campaign=customizing-android-sdk). Pushwoosh provides Private endpoints for customers with Custom Plan subscriptions. To set up Private endpoint for Android SDK, you need to add the following to your **AndroidManifest.xml** file: AndroidManifest.xml ```txt ``` ## Creating a Rich Media queue In case there are several Rich Media pages to display simultaneously (for example, trigger events for two or more In-Apps take place at one moment, or a Rich Media page is being displayed already at the moment a different trigger event occurs), you can set up a queue for Rich Media pages displaying. To create a queue, add the following code to your project: Application.java ```java package com.pushwoosh.testingapp; import com.pushwoosh.RichMediaManager; import com.pushwoosh.exception.PushwooshException; import com.pushwoosh.richmedia.RichMediaPresentingDelegate; import com.pushwoosh.richmedia.RichMedia; import com.pushwoosh.internal.utils.PWLog; import java.util.ArrayDeque; import java.util.concurrent.locks.ReentrantLock; public class DefaultRichMediaPresentingDelegate implements RichMediaPresentingDelegate { private final String TAG = DefaultRichMediaPresentingDelegate.class.getSimpleName(); private ArrayDeque richMediaQueue = new ArrayDeque<>(); private RichMedia currentRichMedia = null; private ReentrantLock reentrantLock; public DefaultRichMediaPresentingDelegate() { PWLog.noise(TAG, "new DefaultRichMediaPresentingDelegate:" + this); reentrantLock = new ReentrantLock(); } @Override public boolean shouldPresent(RichMedia richMedia) { PWLog.noise(TAG, "shouldPresent:" + richMedia); if (currentRichMedia == null) { PWLog.noise(TAG, "currentRichMedia is null"); } if (richMedia.isLockScreen()) { PWLog.noise(TAG, "isLockScreen is true"); return true; } try { reentrantLock.lock(); if (currentRichMedia == null) { PWLog.noise(TAG, "show:" + richMedia); currentRichMedia = richMedia; return true; } else { PWLog.noise(TAG, "add to queue:" + richMedia); richMediaQueue.add(richMedia); return false; } } finally { reentrantLock.unlock(); } } @Override public void onPresent(RichMedia richMedia) { PWLog.noise(TAG, "onPresent" + richMedia); } @Override public void onError(RichMedia richMedia, PushwooshException pushwooshException) { PWLog.error(TAG, pushwooshException + " richMedia:"+richMedia.toString()); tryShowNextRichMediaThreadSafety(); } @Override public void onClose(RichMedia richMedia) { PWLog.noise(TAG, "onClose:" + richMedia); tryShowNextRichMediaThreadSafety(); } private void tryShowNextRichMediaThreadSafety() { try { reentrantLock.lock(); tryShowNextRichMedia(); } finally { reentrantLock.unlock(); } } private void tryShowNextRichMedia() { if (!richMediaQueue.isEmpty()) { currentRichMedia = richMediaQueue.poll(); PWLog.noise(TAG, "try manual show:" + currentRichMedia); RichMediaManager.present(currentRichMedia); } else { PWLog.noise(TAG, "richMediaQueue is empty"); currentRichMedia = null; } } } ``` Important We strongly recommend to set up a queue in **Application** instead of **Activity**. Otherwise, it may create several queues. Note Each [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) method call only allows one In-App to be displayed, and each Push can only be associated with one Rich Media. If you want to show multiple In-Apps, call the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) method the required number of times. ## Custom sound push Note Available for Android 8+ devices. 1. Put your audio file in the proper folder. For the native Android framework, your files should be placed in the `/app/src/main/res/raw` folder. Note Please refer to the corresponding guides to find where to place the audio file in projects built in other frameworks. 2. Create a [Notification Channel.](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/notification-channels/) 3. Select a sound while configuring a push message. 4. Set the Notification Channel the message will belong to. To do so, specify the following in the “Android root params” field:`{"pw_channel": "PUSH NOTIFICATION CHANNEL NAME"} //`` `*`here you need to specify the name for your channel with custom sound`* In case of using remote API, set the parameters as follows within your /createMessage API request: ```java "android_root_params": {"pw_channel": "push"} // here you need to specify the name for your channel with custom sound, for example, "push" for the notifications with push.wav sound. "android_sound": "push" // here you should specify the file name without extension ``` Once you send the push with those params specified, the Notification Channel with the selected sound is created for all devices with Android 8+. Now, to send the push with a custom sound, you have to specify only the channel associated with that sound. ### Proguard rules for custom notification sounds If your app uses proguard for code and resource shrinking, it is important to keep your sound files intact and available for external libraries. If you use **`minifyEnabled = true`** property to your **build.gradle,** add the following rules to your **proguard-rules.pro**: ```plaintext -keep public class your.package.name.R$raw { *; } ``` If you shrink resources of your app on top of code shrinking by using **`shrinkResources=true`** property, you should additionally specify what resources you want to keep. To do that, create a new XML file with any name, save it somewhere in your project (for example, in res/xml), and specify the resource names under the **`tools:keep`** parameter in the **`resources`** tag: ```plaintext ``` ## Complete list of Android SDK meta-data flags To set up a flag, you need to add the meta-data block to your **AndroidManifest.xml** file inside the **application** tag. For example, if you want to set the Pushwoosh application ID, add the following code to your **AndroidManifest.xml** file: AndroidManifest.xml ```txt ``` | Flag | Description | Possible values | | :------------------------------------------------------------: | :------------------------------------------------------------------------------------------------------------------------------------------------: | :------------------------------------------------------------------------------------: | | com.pushwoosh.appid | Sets the Pushwoosh application ID. | XXXXX-XXXXX | | com.pushwoosh.log_level | Sets logging level. For details, refer to [Controlling Log Level](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#controlling-log-level). | NONE / ERROR / WARN / INFO / **DEBUG** (*default*) / NOISE | | com.pushwoosh.base_url | Overrides the Pushwoosh server base url. | [](https://cp.pushwoosh.com/json/1.3/) (*default*) | | com.pushwoosh.notification_service_extension | Custom NotificationServiceExtension. For details, refer to [Customising Notification Open Behavior](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#customising-notification-open-behaviour). | com.myapp.MyNotificationServiceExtension | | com.pushwoosh.notification_factory | Custom NotificationFactory.For details, refer to [Customizing Push Notifications](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#customizing-push-notifications). | com.myapp.MyNotificationFactory | | com.pushwoosh.summary_notification_factory | Custom SummaryNotificationFactory. | com.myapp.MySummaryNotificationFactory | | com.pushwoosh.multi_notification_mode | If true, notification will be grouped. If false, the last received notification will be displayed only. | true / **false** (*default*) | | com.pushwoosh.allow_server_communication | If true, the SDK is allowed to send network requests to Pushwoosh servers. | **true** (*default*) / false | | com.pushwoosh.handle_notifications_using_workmanager | If true, the WorkManager is set to handle notifications. | true / **false** (*default*) | | com.pushwoosh.notification_icon | Custom notification (small) icon resource name. If null, default application icon will be used. | res/drawable-xxhdpi-v11/notification_small_icon.png / null | | com.pushwoosh.notification_icon_color | Notification (small) icon background color. | #FFFFFF | | com.pushwoosh.allow_collecting_device_data | If true, the SDK is allowed to collect and to send device data to Pushwoosh. | **true** (*default*) / false | | com.pushwoosh.allow_collecting_device_os_version | If true, the SDK is allowed to collect and to send device OS version to Pushwoosh. | **true** (*default*) / false | | com.pushwoosh.allow_collecting_device_locale | If true, the SDK is allowed to collect and to send device locale to Pushwoosh. | **true** (*default*) / false | | com.pushwoosh.allow_collecting_device_model | If true, the SDK is allowed to collect and to send device model to Pushwoosh. | **true** (*default*) / false | | com.pushwoosh.in_app_business_solutions_capping | Limits the number of times the *push-unregister* In-App can be shown in a day. | **1** (*default*), 2, …, n | | com.pushwoosh.start_foreground_service | If true, Foreground Service is launched along with the PushwooshLocation.startLocationTracking() call | true / **false** (*default*) | | com.pushwoosh.foreground_service_notification_text | Sets the text of a notification created when the Foreground Service is launched for the “com.pushwoosh.start_foreground_service” key. | **Work in progress** (*default*) | | com.pushwoosh.foreground_service_notification_channel_name | Sets the channel name for the notification created when the Foreground Service is launched for the “com.pushwoosh.start_foreground_service” key. | **Foreground service** (*default)* | | com.pushwoosh.trusted_package_names | Allows sharing Pushwoosh HWID with specified package | ”com.mycompany.myapp1, com.mycompany.myapp2” | ## Deleting Push Notifications via TTL (Time-To-Live) To automatically delete push notifications after a specified time period using TTL (Time-to-Live), follow these steps: 1. Create a Custom NotificationFactory. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#customizing-push-notifications) 2. In the `onGenerateNotification()` method, create a notification using the `Notification.Builder` or `NotificationCompat.Builder` class and call the `setTimeoutAfter` method: ```java public class YourNotificationFactory extends PushwooshNotificationFactory { @Override public Notification onGenerateNotification(@NonNull PushMessage pushMessage) { Notification.Builder builder = new Notification.Builder(getApplicationContext(), addChannel(pushData)); Notification notification = builder.setContentText(pushData.getMessage()) .setContentTitle(title) .setContentText(text) // rest of your notification creation code .setTimeoutAfter(timeout) // time in milliseconds before the notification is canceled .build(); } } ``` ## Share Your Feedback with Us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us via [this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Feature flags in the Android SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/ Read Pushwoosh feature flags and their properties in your Android app, react to updates, and log impressions for comparison. --- `Pushwoosh.getInstance().featureFlags` gives your Android app the [feature flags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) configured for the application in the Control Panel. Pushwoosh evaluates the flag rules on the server for the current device and user, and the SDK returns the result: whether the flag is on for this device and which property values apply. Beta feature Feature flags are in beta and available only on plans that include them. On other plans the server returns no flags, so every flag falls back to the default you pass in code. ## Prerequisites * Pushwoosh Android SDK 6.16.0 or later, integrated as described in the [Android SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/). Feature flags are part of the main `com.pushwoosh:pushwoosh` library, so you don’t add another dependency. Until the SDK is initialized, `get` returns `null`. * At least one flag created in **Audience** → **Feature Flags**. You need its **Key** and the keys and types of its properties. Use the keys exactly as they appear there, letter case included. For example, `get("New_Checkout")` doesn’t find a flag with the key `new_checkout`. ## Read a flag Always pass a default. It applies until the SDK has flags for the device, and whenever the flag doesn’t exist, is archived, or the plan doesn’t include feature flags. ```kotlin import com.pushwoosh.Pushwoosh val flags = Pushwoosh.getInstance().featureFlags val checkout = flags.get("new_checkout") if (checkout != null && checkout.isEnabled) { showNewCheckout( buttonColor = checkout.getString("button_color", "blue"), maxItems = checkout.getNumber("max_items", 3.0).toInt(), ) } else { showOldCheckout() } ``` To check only whether the flag is on, use `isEnabled`: ```kotlin val isNewCheckout = Pushwoosh.getInstance().featureFlags.isEnabled("new_checkout", false) ``` In Java, call `Pushwoosh.getInstance().getFeatureFlags()`. ## What a flag returns `get(key)` returns a `FeatureFlag` object, or `null` when the SDK has no flags for the device yet or the flag isn’t in the answer. An archived flag is not in the answer. `get` reads only the stored flags and never waits for the network. The object has these fields: | Field | Type | Description | | :---------- | :------ | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | String | The flag key. | | `isEnabled` | Boolean | Whether the flag is on for this device. | | `ruleId` | String | The rule that decided the result, for example `r_1a2b3c4d`. `default` when no rule matched the device, and an empty string when the flag is killed. | Read property values with the getter that matches the property type. Each getter returns your default when the property is missing or holds a value of another type. When the flag is off, the properties hold the default values set in the Control Panel. | Method | Returns | | :-------------------------------------- | :------------------------------------------------------------------------ | | `getString(property, defaultValue)` | The value of a **String** property. | | `getNumber(property, defaultValue)` | The value of a **Number** property, as a `Double`. | | `getBool(property, defaultValue)` | The value of a **Boolean** property. | | `getJsonObject(property, defaultValue)` | The value of a **JSON** property that holds an object, as a `JSONObject`. | | `getJsonArray(property, defaultValue)` | The value of a **JSON** property that holds an array, as a `JSONArray`. | `isEnabled(key, defaultValue)` returns `defaultValue` when `get(key)` returns `null`. ## Keep flags up to date The SDK stores the last answer on the device for up to 24 hours. The stored flags are valid only for the User ID they were requested for. The SDK refreshes them: * When a session starts, that is, when the app comes to the foreground for the first time or after at least 30 minutes in the background. * Every hour while the app is in the foreground. * After the device registers for the first time. * Right after the User ID changes with `setUserId`. The stored flags of the previous user are discarded, so a new user never sees them. To react to new values, register a listener. It’s called after a refresh that changed any flag value: ```kotlin import com.pushwoosh.featureflags.FeatureFlagsUpdateListener private val flagsListener = FeatureFlagsUpdateListener { render() } override fun onStart() { super.onStart() Pushwoosh.getInstance().featureFlags.addUpdateListener(flagsListener) } override fun onStop() { Pushwoosh.getInstance().featureFlags.removeUpdateListener(flagsListener) super.onStop() } ``` To request the flags yourself, for example after an action that may move the user into a different segment, call `refresh`. While the stored flags are valid, it sends at most one request per 5 minutes and answers from the stored flags within that window. The callback runs on the main thread: ```kotlin Pushwoosh.getInstance().featureFlags.refresh { result -> if (!result.isSuccess) { val status = result.exception?.httpStatus // the stored flags, or your defaults, stay in effect } render() } ``` On failure, `result.exception` is a `FeatureFlagsException`. Its `httpStatus` is `0` when the request got no HTTP response, and `200` when the server answered but the answer couldn’t be used. If Pushwoosh can’t match the device’s segments in time, it returns a partial answer. The SDK doesn’t store it, and the next refresh brings the full answer. When a flag is killed in the Control Panel, devices get it off with default property values at the next refresh. When the SDK can’t reach the server, it keeps using the stored flags until they are 24 hours old, and your defaults after that. ## Log impressions An impression tells Pushwoosh that a user actually reached the code a flag controls, so you can compare users who got the feature with those who didn’t. Call `logImpression(key)` at the moment the user sees the feature or its old version, for both the enabled and the disabled branch, because users with the flag off are your comparison group: ```kotlin val flags = Pushwoosh.getInstance().featureFlags val isOn = flags.isEnabled("new_checkout", false) flags.logImpression("new_checkout") if (isOn) { showNewCheckout() } else { showOldCheckout() } ``` The SDK posts the `PW_FeatureFlagImpression` event with these attributes: | Attribute | Type | Value | | :--------- | :------ | :------------------------------------------------ | | `flag_key` | string | The flag key. | | `rule_id` | string | The rule that decided the result, as in `ruleId`. | | `enabled` | boolean | Whether the flag was on. | You don’t need to create the event in advance, because Pushwoosh adds `PW_FeatureFlagImpression` with these attributes to **Audience** → **Events** when the first impression arrives. The SDK sends one impression per flag, rule, and state in a session, and sends nothing when there is no flag for the key. Impressions also let marketers build a segment of users who reached the feature: the **saw the feature (30 days)** state of the [feature flag condition](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature). ## Method reference `Pushwoosh.getInstance().featureFlags` has these methods: | Method | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `get(key)` | Returns the `FeatureFlag` for the key, or `null`. Reads the stored flags only. | | `isEnabled(key, defaultValue)` | Returns whether the flag is on, or `defaultValue` when there is no flag. | | `refresh(callback)` | Requests the flags, throttled to once per 5 minutes while the stored flags are valid. The callback gets a `Result` with a `FeatureFlagsException` on failure, and can be `null`. | | `addUpdateListener(listener)` | Calls `listener` after every refresh that changed a flag value. | | `removeUpdateListener(listener)` | Removes a listener added with `addUpdateListener`. | | `logImpression(key)` | Posts a `PW_FeatureFlagImpression` event for the flag’s current rule and state. | --- # Android SDK basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/ Learn how to integrate the Pushwoosh SDK into your Android application with Firebase Cloud Messaging step by step --- This section contains information on how to integrate the Pushwoosh SDK into your Android application. ## Prerequisites To integrate the Pushwoosh Android SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `project number` (also known as Sender ID), `google-services.json` file and `package name` from your Firebase project. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Set up Firebase Cloud Messaging First, connect your Android project with Firebase: * Open your project in Android Studio * Navigate to **Tools > Firebase > Cloud Messaging** and click “Set up Firebase Cloud Messaging” * Follow the wizard instructions. In the final step, the wizard will add the necessary dependencies to your `build.gradle` files ### 2. Add Pushwoosh SDK Add the pushwoosh library dependency to your application’s build.gradle: build.gradle ```groovy implementation 'com.pushwoosh:pushwoosh-firebase:6.+' ``` Replace ”+” with the current version of [Pushwoosh Android SDK](https://github.com/Pushwoosh/pushwoosh-android-sdk/releases/latest) Have your own FirebaseMessagingService? If your app already defines its own `FirebaseMessagingService`, for example to route push notifications to different screens, don’t let it swallow Pushwoosh push notifications. Pass every message through `PushwooshFcmHelper.onMessageReceived(this, remoteMessage)` for push notifications where `PushwooshFcmHelper.isPushwooshMessage(remoteMessage)` is true. Skip that call and Pushwoosh loses visibility into the message: whether it still displays depends on your own handler. If it builds a notification for the data anyway, the user sees it normally. If it doesn’t, nothing displays. Either way, Pushwoosh never learns the message was delivered or opened, so delivery and open statistics for it stay at zero. See [Using Pushwoosh SDK with other FCM services](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#using-pushwoosh-sdk-with-other-fcm-services) for the full routing example. ### 3. Add Pushwoosh metadata to AndroidManifest.xml Open your AndroidManifest.xml file and add the following metadata inside the `` tag: ```xml ``` Where: * `com.pushwoosh.appid` is your Pushwoosh Application Code * `com.pushwoosh.apitoken` is your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 4. Initialization code Register for push notifications by adding this code to your initialization logic: ```java Pushwoosh.getInstance().registerForPushNotifications() ``` ### 5. Run the project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notifications In the Pushwoosh SDK, there are two callbacks designed for handling push notifications: * `onMessageReceived`: This method is invoked when a push notification is received. * `onMessageOpened`: This method is called when the user interacts with (opens) the notification These callbacks enable developers to manage the reception and user interaction with push notifications within their applications: AndroidManifest.xml ```xml ``` * Kotlin ```kotlin import android.util.Log import com.pushwoosh.notification.NotificationServiceExtension import com.pushwoosh.notification.PushMessage class YourNotificationServiceExtension : NotificationServiceExtension() { override fun onMessageReceived(data: PushMessage): Boolean { Log.d("Pushwoosh", "onMessageReceived: ${data.toJson()}") return false } override fun onMessageOpened(message: PushMessage) { Log.d("Pushwoosh", "onMessageOpened: ${message.toJson()}") } } ``` * Java ```java import android.util.Log; import com.pushwoosh.notification.NotificationServiceExtension; import com.pushwoosh.notification.PushMessage; public class YourNotificationServiceExtensionJava extends NotificationServiceExtension { @Override public boolean onMessageReceived(PushMessage data) { Log.d("Pushwoosh", "onMessageReceived: " + data.toJson()); return false; } @Override public void onMessageOpened(PushMessage message) { Log.d("Pushwoosh", "onMessageOpened: " + message.toJson()); } } ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty * Kotlin ```kotlin import com.pushwoosh.Pushwoosh import com.pushwoosh.tags.TagsBundle import java.util.Date class Registration { public void afterUserLogin(User user) { Pushwoosh pushwoosh = Pushwoosh.getInstance() // Set user ID if (user.getUserId() != null) { pushwoosh.setUserId(user.getUserId()) } // Set user email if (user.getEmail() != null) { pushwoosh.setEmail(user.getEmail()) } // Setting additional user information as tags for Pushwoosh if (user.getUserDetails() != null) { val details = user.getUserDetails() val tagsBuilder = TagsBundle.Builder() if (details.getAge() != null) { tagsBuilder.putInt("age", details.getAge()) } if (details.getUserName() != null) { tagsBuilder.putString("name", details.getUserName()) } if (details.lastLoginDate() != null) { tagsBuilder.putDate("last_login", details.getLastLoginDate()) } // Build and send tags val tags = tagsBuilder.build() if (!tags.isEmpty()) { pushwoosh.setTags(tags) } } } } ``` * Java ```java import com.pushwoosh.Pushwoosh; import com.pushwoosh.tags.TagsBundle; import java.util.Date; public class Registration { public void afterUserLogin(User user) { Pushwoosh pushwoosh = Pushwoosh.getInstance(); // Set user ID if (user.getUserId() != null) { pushwoosh.setUserId(user.getUserId()); } // Set user email if (user.getEmail() != null) { pushwoosh.setEmail(user.getEmail()); } // Set user SMS number if (user.getSmsNumber() != null) { pushwoosh.registerSMSNumber(user.getSmsNumber()); } // Set user WhatsApp number if (user.getWhatsAppNumber() != null) { pushwoosh.registerWhatsappNumber(user.getWhatsAppNumber()); } // Setting additional user information as tags for Pushwoosh if (user.getUserDetails() != null) { UserDetails details = user.getUserDetails(); TagsBundle.Builder tagsBuilder = new TagsBundle.Builder(); if (details.getAge() != null) { tagsBuilder.putInt("age", details.getAge()); } if (details.getUserName() != null) { tagsBuilder.putString("name", details.getUserName()); } if (details.getLastLoginDate() != null) { tagsBuilder.putDate("last_login", details.getLastLoginDate()); } // Build and send tags TagsBundle tags = tagsBuilder.build(); if (!tags.isEmpty()) { pushwoosh.setTags(tags); } } } } ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. * Kotlin ```kotlin import com.pushwoosh.Pushwoosh import com.pushwoosh.tags.TagsBundle import java.util.List class UpdateUser { public void afterUserUpdateProfile(User user) { val pushwoosh = Pushwoosh.getInstance() val tagsBuilder = TagsBundle.Builder() // Set list of favorite categories if (user.favoriteCategories != null && user.favoriteCategories.isNotEmpty()) { tagsBuilder.putList("favorite_categories", user.getFavoriteCategories()) } // Set payment information tagsBuilder.putBoolean("is_subscribed", user.isSubscribed()) tagsBuilder.putString("payment_status", user.getPaymentStatus()) tagsBuilder.putString("billing_address", user.getBillingAddress()) // Build and send tags pushwoosh.setTags(tagsBuilder.build()) } } ``` * Java ```java import com.pushwoosh.Pushwoosh; import com.pushwoosh.tags.TagsBundle; import java.util.List; public class UpdateUser { public void afterUserUpdateProfile(User user) { Pushwoosh pushwoosh = Pushwoosh.getInstance(); TagsBundle.Builder tagsBuilder = TagsBundle.Builder(); // Set list of favorite categories if (user.getFavoriteCategories() != null && !user.getFavoriteCategories().isEmpty()) { tagsBuilder.putList("favorite_categories", user.getFavoriteCategories()); } // Set payment information tagsBuilder.putBoolean("is_subscribed", user.isSubscribed()); tagsBuilder.putString("payment_status", user.getPaymentStatus()); tagsBuilder.putString("billing_address", user.getBillingAddress()); // Build and send tags pushwoosh.setTags(tagsBuilder.build()); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions * Kotlin ```kotlin import com.pushwoosh.Pushwoosh import com.pushwoosh.inapp.InAppManager import com.pushwoosh.tags.TagsBundle import java.util.Date class Registration { public void afterUserLogin(User user) { if (user.getUserName() != null && user.getLastLoginDate() != null) { val attributes = TagsBundle.Builder() .putString("name", user.getUserName()) .putDate("last_login", user.getLastLoginDate()) .build() InAppManager.getInstance().postEvent("login", attributes) } } public void afterUserPurchase(User user, Product product) { Pushwoosh pushwoosh = Pushwoosh.getInstance() // Track purchase event val purchaseAttributes = TagsBundle.Builder() .putString("product_id", product.getId()) .putString("product_name", product.getName()) .putDouble("price", product.getPrice()) .putInt("quantity", product.getQuantity()) .build() InAppManager.getInstance().postEvent("purchase", purchaseAttributes) // Set user tags val tagsBuilder = TagsBundle.Builder() tagsBuilder.putDate("last_purchase_date", Date()) val lifetimeSpend = getCurrentLifetimeSpend() + product.getPrice() tagsBuilder.putDouble("lifetime_spend", getCurrentLifetimeSpend()) pushwoosh.setTags(tagsBuilder.build()) } } ``` * Java ```java import com.pushwoosh.Pushwoosh; import com.pushwoosh.tags.TagsBundle; import java.util.Date; public class Registration { public void afterUserLogin(User user) { if (user.getUserName() != null && user.getLastLoginDate() != null) { TagsBundle attributes = new TagsBundle.Builder() .putString("name", user.getUserName()) .putDate("last_login", user.getLastLoginDate()) .build(); Pushwoosh.getInstance().postEvent("login", attributes); } } public void afterUserPurchase(User user, Product product) { Pushwoosh pushwoosh = Pushwoosh.getInstance(); // Track purchase event TagsBundle purchaseAttributes = new TagsBundle.Builder() .putString("product_id", product.getId()) .putString("product_name", product.getName()) .putDouble("price", product.getPrice()) .putInt("quantity", product.getQuantity()) .build(); Pushwoosh.getInstance().postEvent("purchase", purchaseAttributes); // Set user tags TagsBundle.Builder tagsBuilder = new TagsBundle.Builder(); tagsBuilder.putDate("last_purchase_date", new Date()); double lifetimeSpend = getCurrentLifetimeSpend() + product.getPrice(); tagsBuilder.putDouble("lifetime_spend", getCurrentLifetimeSpend()); pushwoosh.setTags(tagsBuilder.build()); } } ``` ### Rich Media Rich media refers to interactive and multimedia content, such as images, videos, or HTML, used in notifications and in-app messages to enhance user engagement To enable the **Modal Rich Media** feature, you need to configure it in your application’s `AndroidManifest.xml` file by adding the following metadata entry: AndroidManifest.xml ```xml ``` * Kotlin ```kotlin import android.util.Log import com.pushwoosh.richmedia.RichMedia import com.pushwoosh.richmedia.RichMediaManager import com.pushwoosh.richmedia.RichMediaPresentingDelegate import com.pushwoosh.inapp.view.config.ModalRichmediaConfig import com.pushwoosh.inapp.view.config.enums.* class MainActivity : AppCompatActivity(), RichMediaPresentingDelegate { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) RichMediaManager.setDelegate(this) // Configure the modal rich media val config = ModalRichmediaConfig() .setViewPosition(ModalRichMediaViewPosition.BOTTOM) .setPresentAnimationType(ModalRichMediaPresentAnimationType.SLIDE_UP) .setDismissAnimationType(ModalRichMediaDismissAnimationType.SLIDE_DOWN) // Apply the configuration RichMediaManager.setDefaultRichMediaConfig(config) } override fun shouldPresent(richMedia: RichMedia): Boolean { Log.d("Pushwoosh", "Rich media will be presented with: ${richMedia.content}") return true } override fun onPresent(richMedia: RichMedia) { Log.d("Pushwoosh", "Rich media has been presented: ${richMedia.content}") } override fun onClose(richMedia: RichMedia) { Log.d("Pushwoosh", "Rich media has been closed: ${richMedia.content}") } override fun onError(richMedia: RichMedia, pushwooshException: PushwooshException) { Log.e("Pushwoosh", "Failed to present rich media: ${richMedia.content}. Error: ${pushwooshException.message}") } } ``` * Java ```java import android.util.Log; import com.pushwoosh.richmedia.RichMedia; import com.pushwoosh.richmedia.RichMediaManager; import com.pushwoosh.richmedia.RichMediaPresentingDelegate; import com.pushwoosh.inapp.view.config.ModalRichmediaConfig; import com.pushwoosh.inapp.view.config.enums.*; public class MainActivity extends AppCompatActivity implements RichMediaPresentingDelegate { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); RichMediaManager.setDelegate(this); // Configure the modal rich media ModalRichmediaConfig config = new ModalRichmediaConfig() .setViewPosition(ModalRichMediaViewPosition.BOTTOM) .setPresentAnimationType(ModalRichMediaPresentAnimationType.SLIDE_UP) .setDismissAnimationType(ModalRichMediaDismissAnimationType.SLIDE_DOWN); // Apply the configuration RichMediaManager.setDefaultRichMediaConfig(config); } @Override public boolean shouldPresent(RichMedia richMedia) { Log.d("Pushwoosh", "Rich media will be presented with: " + richMedia.getContent()); return true; } @Override public void onPresent(RichMedia richMedia) { Log.d("Pushwoosh", "Rich media has been presented: " + richMedia.getContent()); } @Override public void onClose(RichMedia richMedia) { Log.d("Pushwoosh", "Rich media has been closed: " + richMedia.getContent()); } @Override public void onError(RichMedia richMedia, PushwooshException pushwooshException) { Log.e("Pushwoosh", "Failed to present rich media: " + richMedia.getContent() + ". Error: " + pushwooshException.getMessage()); } } ``` ## Troubleshooting If a push isn’t delivered to Android devices, see [Android, Chrome, Firefox errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/android-chrome-firefox-errors/) for the error codes and how to fix each one. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # Android SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/quick-start/ Get started with the Pushwoosh Android SDK using this quick and simple example. --- Get started with the Pushwoosh Android SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh Android SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `project number` (also known as Sender ID), `google-services.json` file and `package name` from your Firebase project. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project Clone the example project from GitHub: ```bash git clone https://github.com/Pushwoosh/pushwoosh-android-sample ``` ## Configure the project 1. Place your **google-services.json** file in the pushwoosh-demoapp/app directory. 2. In your **build.gradle** (Module: app), update the `applicationId` to match your package name. 3. In **res/values/strings.xml**, replace the placeholder values with your specific details: * `pushwoosh_app_id`: Use your Pushwoosh App Code. * `pushwoosh_api_token`: Use your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) strings.xml ```xml demoapp Actions Settings _YOUR_PUSHWOOSH_APP_ID_ _YOUR_PUSHWOOSH_DEVICE_API_TOKEN_ ``` ## Run the project 1. Build and run the project. 2. The app will have two tabs: “Actions” and “Settings”. Navigate to the **Settings** tab. 3. Tap the **Register for Pushes** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. You should see a log entry like this: Logcat ```plaintext Log level: INFO [RequestPermissionHelper] Requesting permissions Pushwoosh SDK initialized successfully HWID: __HWID_OF_YOUR_DEVICE__ APP_CODE: __YOUR_PUSHWOOSH_APP_ID__ PUSHWOOSH_SDK_VERSION: __PUSHWOOSH_SDK_VERSION_INSTALLED__ FIREBASE_PROJECT_ID: __YOUR_FIREBASE_PROJECT_ID__ PUSH_TOKEN: User ID "__USER_ID_OF_THE_DEVICE__" successfully set [NotificationManager] Registered for push notifications: __DEVICE_PUSH_TOKEN__ ``` ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. ## What’s next To learn more, please refer to the [basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/) --- # Resolving Google Play Services conflicts **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/resolving-google-play-services-conflicts/ Learn how to resolve conflicts between different plugins, frameworks, and google-play-services libraries in your Android project --- Different plugins for different frameworks can use different versions of the google-play-services library, causing your project to fail to compile or crash because the library version is outdated. The following sections provide solutions for different plugins. ## Library location You can find the Google Play Services library at `/extras/google`. The library is available in two formats: 1. **Monolithic jar file** (containing all Google Play Services components in a single file): ```plaintext sdk/extras/google/google_play_services/libproject/google-play-services_lib/libs/google-play-services.jar ``` 2. **Modular aar files** (Android Archive), where each component is in a separate file: ```plaintext sdk/extras/google/m2repository/com/google/android/gms/… ``` An **aar** file can be stripped to a **jar** file (not all frameworks support aar at the moment) by unzipping the file (aar is a zip archive) and taking **classes.jar** - the code of the library. ### 1. Cordova All google play services conflicts on Cordova are resolved automatically using gradle. Sometimes (e.g., different plugins use different major versions of library) resolution may fail and there might appear two different versions of the same library in the resulting build. ### 2. Unity, Appcelerator Titanium You can find Google Play Services library in the plugin (or sample project). They are easy to update/change/remove to solve possible conflicts: * Unity: * Unity and Google Analytics plugin: Google analytics plugin uses an outdated google play library, which needs to be updated. More info here: ```bash rm -rf Assets/Plugins/Android/google-play-services_lib/libs rm -rf Assets/Plugins/Android/google-play-services_lib/res cp ${ANDROID_HOME}/extras/google/m2repository/com/google/android/gms/play-services-analytics/8.4.0/play-services-analytics-8.4.0.aar Assets/Plugins/Android/ cp ${ANDROID_HOME}/extras/google/m2repository/com/google/android/gms/play-services-ads/8.4.0/play-services-ads-8.4.0.aar Assets/Plugins/Android/ ``` * Appcelerator Titanium (note the version of the module may change faster than this document!): ### 3. Adobe Air Take the ANE extension, extract it, modify it, and repackage it. Use the following example with **PushNotification.ane**: ```bash unzip PushNotification.ane -d tmp ``` This command extracts the contents of the .ane file to the `tmp` directory. Now in the *tmp/META-INF/ANE/Android-ARM/* and *tmp/META-INF/ANE/Android-x86/* you can find all the libraries: android-support_, play-services_, that may have been modified/updated/removed. If you still follow the link to the document above, change `tmp/META-INF/ANE/Android-ARM/platform.xml` and `tmp/META-INF/ANE/Android-x86/platform.xml`, then zip the extension back and everything should work normally. If you get the error: > PushNotification.ane is not a valid native extension file. Repackage the extension using the following steps: 1. Delete `tmp/META-INF/ANE/Android-ARM/platform.xml` and `tmp/META-INF/ANE/Android-x86/platform.xml` 2. Modify as per guide in the link above `tmp/META-INF/ANE/Android-ARM/android-depends.xml` and `tmp/META-INF/ANE/Android-x86/android-depends.xml` instead 3. Run the following bash commands: ```bash #rename platform.xml on iOS, required to repackage the extension mv tmp/META-INF/ANE/iPhone-ARM/platform.xml tmp/META-INF/ANE/iPhone-ARM/platform-iOS.xml #pack the extension back with adt cd tmp/META-INF/ANE/ “${flex.sdk}/bin/adt" -package -target ane PushNotification.ane extension.xml -swc ../../../../SWC-Pushwoosh/PushNotification.swc -platform iPhone-ARM -platformoptions iPhone-ARM/platform-iOS.xml -C iPhone-ARM/ . -platform Android-ARM -platformoptions Android-ARM/android-depends.xml -C Android-ARM/ . -platform Android-x86 -C Android-x86/ . -platformoptions Android-x86/android-depends.xml -platform default -C default/ . ``` You can find PushNotification.swc here: ### 4. Unreal Engine Unreal Engine and Pushwoosh Plugin will almost always conflict with each other. This is explained by Unreal Engine containing Google Play services by default, and different versions of the Engine use different versions of these libs. Pushwoosh plugin uses these libs as well, however a different subset. Always be careful when integrating Pushwoosh Plugin for Unreal Engine. Even if your project seems to compile and work well, ensure that both Pushwoosh and Unreal Engine use the same version of Google Play Services. Using different versions of google-play-services in one project results in undefined behavior. To resolve a conflict you should do the following: **1.** Determine which Google Play services libraries are already used in a project. Check `${UNREAL_PROJECT}/Intermediate/Android/APK/JavaLibs` and `${UNREAL_PROJECT}/Intermediate/Android/APK/libs` directories. For example, in the [sample project](https://github.com/Pushwoosh/pushwoosh-unreal-engine) created using Unreal Engine 4.15 there are following libraries: JavaLibs: * downloader_library * play-services-ads-lite-9.2.0 * play-services-auth-base-9.2.0 * play-services-basement-9.2.0 * play-services-drive-9.2.0 * play-services-gass-9.2.0 * play-services-plus-9.2.0 * support-v4-23.0.0 * play-services-ads-9.2.0 * play-services-auth-9.2.0 * play-services-base-9.2.0 * play-services-clearcut-9.2.0 * play-services-games-9.2.0 * play-services-nearby-9.2.0 * play-services-tasks-9.2.0 * pushwoosh-ue-plugin // Added by Pushwoosh plugin libs: * SystemUtils.jar * VrApi.jar * play-services-gcm-9.2.0.jar // Added by Pushwoosh plugin * play-services-iid-9.2.0.jar // Added by Pushwoosh plugin **2.** If versions of play-services* libraries used in Pushwoosh and Unreal Engine do not match, remove these libraries from [Pushwoosh plugin](https://github.com/Pushwoosh/pushwoosh-unreal-engine/tree/master/Plugins/Pushwoosh/lib/Android/JavaLibs/pushwoosh-ue-plugin). **3.** Determine which missing libraries you should add to Pushwoosh Plugin. The plugin requires android-support-v4, play-services-base, play-services-basement, play-services-gcm, play-services-iid libraries. For the sample project only **play-services-gcm** and **play-services-iid** do not overlap, so they are the only libraries that should be added in this particular case. **4.** Add the library by placing the .jar file from Android SDK in . The location of Google Play libraries is described at the beginning of this article. ## Share your feedback with us Your feedback helps us create a better experience. If you face any difficulties during the SDK integration process, share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Huawei integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/huawei-integration/ How to integrate Pushwoosh SDK into your Huawei project --- ## Requirements * A [Pushwoosh account](https://go.pushwoosh.com/) * A [Huawei developer account](https://developer.huawei.com) * A [Huawei AppGallery Connect project](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject) * A [Pushwoosh Application](https://go.pushwoosh.com/cp/applications) with [configured Huawei platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration/) and its code in format **XXXXX-XXXXX** * [Android Studio](https://developer.android.com/studio) * A Huawei device with Huawei Mobile Services installed ## Integration ### Get agconnect-services.json **1.** Go to [AppGallery Connect -> Projects List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject) and select a project to configure. **2.** Press **agconnect-services.json** in the App information section to download a configuration file. **3.** Put the downloaded file into your Android Studio project (YOUR_PROJECT/app/agconnect-services.json). ### Generate Signing Certificate Fingerprint Follow [Generating a Signing Certificate Fingerprint](https://developer.huawei.com/consumer/en/doc/development/HMS-Guides/Preparations#h2-1575707383351) to generate a signing certificate fingerprint. ### Add the `pushwoosh-huawei` module Add the `pushwoosh-huawei` module to your app-level `build.gradle`: ```plaintext implementation 'com.pushwoosh:pushwoosh-huawei:6.+' ``` Replace ”+” with the current version of Pushwoosh: ​ ### Add Huawei dependencies **1.** Add `maven {url 'https://developer.huawei.com/repo/'}` to your **root-level** `build.gradle (Project)` under `buildscript { repositories }` and `allprojects { repositories }`. **2.** Add `classpath 'com.huawei.agconnect:agcp:1.3.1.300'` under `buildscript { dependencies }`. Make sure all the following three lines added to your **root-level** `build.gradle`: **3.** Add `implementation 'com.huawei.hms:push:6.1.0.300'` to your **app-level** `build.gradle` (app/build.gradle) under the `dependencies` section. **4.** Add `apply plugin: 'com.huawei.agconnect'` to the very bottom of the **app-level** `build.gradle` file. Make sure there are two following lines in your **app-level** `build.gradle`: Caution **Sync Gradle** Make sure to press **Sync Now** on the banner that pops up after saving. ### Subscribe to push notifications ```plaintext Pushwoosh.getInstance().registerForPushNotifications(); ``` ## Troubleshooting If a push isn’t delivered to Huawei devices, see [Huawei errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/huawei-errors/) for the error codes and how to fix each one. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). ## Next steps [Customize Android SDK ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50) --- # Live Updates on Android **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/live-updates/ Send Android Live Updates with Pushwoosh — progress-style notifications on Android 16+ for delivery, ride-hailing, and order tracking. --- Pushwoosh supports Android Live Updates through the `pushwoosh-liveupdates` module (SDK 6.9.0 and later). A Live Update is an ongoing, progress-style notification that the system promotes on the lock screen, in the notification drawer, and as a status chip in the status bar, so users can follow an activity without opening your app. The whole lifecycle is driven from the server: your backend sends a push when the activity starts, more pushes as it progresses, and a final push when it ends. The SDK renders each one automatically. ## What are Live Updates Live Updates were introduced in Android 16 (API 36) as a way to surface a user-initiated, time-sensitive activity from start to finish. They build on the platform’s progress-centric notifications and `Notification.ProgressStyle` API. Conceptually they are the Android counterpart of iOS Live Activities. This page covers the Pushwoosh integration only. For the platform behavior, promotion rules, and design guidance, see the official Android documentation: * [Progress-centric notifications](https://developer.android.com/about/versions/16/features/progress-centric-notifications) * [Create live update notifications (Views)](https://developer.android.com/develop/ui/views/notifications/live-update) * [Create live update notifications (Compose)](https://developer.android.com/develop/ui/compose/notifications/live-update) ## When to use Live Updates Live Updates are meant for an activity that is ongoing, user-initiated, and time-sensitive — something with a clear start and end that the user actively cares about right now. Typical scenarios for Pushwoosh customers: * **Food delivery** — order accepted, being prepared, out for delivery, arriving. * **Ride-hailing and taxi** — driver assigned, en route, arriving, trip in progress. * **Order and shipment tracking** — live status of an order that is actively in transit. * **Live sports and media** — match score and time as the game unfolds. * **Fitness** — an active workout or run with elapsed time and progress. * **Fintech** — a transaction or verification flow moving through its stages. Because the lifecycle is driven by real events that your backend already knows about (an order changes status, a courier moves), a Live Update is usually one API call wired into your existing event flow — not something a person sends by hand. Caution Do not use Live Updates for promotions, chat messages, or ambient information, and never repost a Live Update the user has dismissed. Android may revoke the app’s ability to post promoted notifications. Follow the [Android guidance on appropriate use](https://developer.android.com/develop/ui/views/notifications/live-update#best-practices). ## Requirements * Android 16 (API 36) or newer. On older devices the module stays inactive and every Live Update API call is a safe no-op. * Pushwoosh Android SDK 6.9.0 or later. ## Add the pushwoosh-liveupdates module Add the dependency to your **app/build.gradle**: ```groovy dependencies { implementation 'com.pushwoosh:pushwoosh-liveupdates:' } ``` Replace `` with the current version from [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-liveupdates). The module is discovered automatically at startup. It declares the required `POST_PROMOTED_NOTIFICATIONS` permission, registers its own notification channel, and intercepts Live Update pushes before the default notification path. There is no extra initialization code — the SDK sets the ongoing and promoted flags, downloads the large icon, maps action buttons, and posts the notification for you. ## Send a Live Update You send Live Updates through [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) by adding a `live_update` object to the `android` content block. Use a `transactional` request — a Live Update targets the specific user whose activity it tracks. The `schedule` field is required; `{ "after": "0s" }` sends immediately. The lifecycle has three operations, set in `live_update.op`: * `OPERATION_START` — first push for an activity. Posts the ongoing notification. * `OPERATION_UPDATE` — a later push for the same activity. Refreshes it in place, silently. * `OPERATION_END` — terminal push. Dismisses the notification. All pushes that belong to the same activity must share the same `live_update.id`. That id ties the updates together and is also what you use to dismiss the update from the app. Each push fully describes the notification — nothing carries over from the previous push. Resend every field you want to keep, such as the segments and the large icon, with each `OPERATION_UPDATE`; an omitted field is rendered as absent. ### Live Update parameters These keys go inside the `live_update` object of the `android` content block. Title, body, and large icon use the standard Android push fields (`title`, `body`, `custom_icon`), sent alongside `live_update`. | Parameter | Type | Description | | ------------------------ | ------ | ----------------------------------------------------------------------------------------- | | `op` | string | Lifecycle operation: `OPERATION_START`, `OPERATION_UPDATE`, or `OPERATION_END`. Required. | | `id` | string | Stable activity id shared by all pushes of one Live Update. Required. | | `progress` | int | Progress value, measured against the summed segment lengths. | | `progress_indeterminate` | bool | Show an indeterminate animation instead of a concrete value. | | `progress_bar` | bool | Show the progress bar at all. Defaults to `true`. | | `segments` | array | Ordered progress segments, each `{ "color": "#RRGGBB", "length": N }`. | | `extras` | object | Arbitrary data passed through to a custom style provider. | | `when` | int64 | Header time anchor, in epoch milliseconds. | | `chronometer` | bool | Show the header time as a running timer. | | `chronometer_count_down` | bool | A running timer counts down instead of up. | | `show_when` | bool | Show the header time column at all. Defaults to `true`. | Note The `op` value must be one of the exact enum names `OPERATION_START`, `OPERATION_UPDATE`, or `OPERATION_END` — short forms are ignored. Each field carries its native JSON type: `segments` is a JSON array and `extras` a JSON object, not encoded strings. The four time fields combine as follows: with `show_when` set to `false` the time is hidden; otherwise `when` is the anchor, `chronometer` turns it into a live counter, and `chronometer_count_down` makes that counter run backwards. ### Start push ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["ANDROID"], "users": { "list": ["customer-42"] }, "payload": { "content": { "localized_content": { "default": { "android": { "title": "Order #4521", "body": "We are preparing your order", "custom_icon": "https://example.com/restaurant.png", "live_update": { "op": "OPERATION_START", "id": "order_4521", "progress": 1, "segments": [ { "color": "#34A853", "length": 3 }, { "color": "#FBBC05", "length": 4 }, { "color": "#4285F4", "length": 3 } ], "extras": { "eta": "18:40" } } } } } } }, "schedule": { "after": "0s" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL" } }' ``` ### Update push Send an `OPERATION_UPDATE` with the same `id` whenever the activity moves forward. Repeat the segments and the icon — an update that omits them renders without them. ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["ANDROID"], "users": { "list": ["customer-42"] }, "payload": { "content": { "localized_content": { "default": { "android": { "title": "Order #4521", "body": "Your courier is on the way", "custom_icon": "https://example.com/restaurant.png", "live_update": { "op": "OPERATION_UPDATE", "id": "order_4521", "progress": 7, "segments": [ { "color": "#34A853", "length": 3 }, { "color": "#FBBC05", "length": 4 }, { "color": "#4285F4", "length": 3 } ] } } } } } }, "schedule": { "after": "0s" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL" } }' ``` ### End push The terminal push only needs the operation and the id. ```bash curl -X POST https://api.pushwoosh.com/messaging/v2/notify \ -H "Authorization: Token YOUR_API_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "transactional": { "application": "XXXXX-XXXXX", "platforms": ["ANDROID"], "users": { "list": ["customer-42"] }, "payload": { "content": { "localized_content": { "default": { "android": { "live_update": { "op": "OPERATION_END", "id": "order_4521" } } } } } }, "schedule": { "after": "0s" }, "message_type": "MESSAGE_TYPE_TRANSACTIONAL" } }' ``` ## Customize the appearance The SDK ships a default progress style built from `progress`, `progress_indeterminate`, and `segments`. To take full control of the progress bar, implement `LiveUpdateProgressStyleProvider` and shape a `Notification.ProgressStyle` yourself. The provider is the single customization point. The SDK still owns channel setup, the ongoing and promoted flags, the large icon, action buttons, and the header time — a custom provider can only shape the progress bar, so it cannot break promotion eligibility. It must be stateless: derive the returned style only from the supplied `LiveUpdateState`. If it throws, the SDK falls back to the default style and the notification still posts. * Java ```java import android.app.Notification; import androidx.annotation.NonNull; import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider; import com.pushwoosh.liveupdates.LiveUpdateSegment; import com.pushwoosh.liveupdates.LiveUpdateState; import java.util.List; public class OrderStyleProvider implements LiveUpdateProgressStyleProvider { @NonNull @Override public Notification.ProgressStyle createStyle(@NonNull LiveUpdateState state) { Notification.ProgressStyle style = new Notification.ProgressStyle(); if (state.getProgress() != null) { style.setProgress(state.getProgress()); } style.setProgressIndeterminate(state.isProgressIndeterminate()); List segments = state.getSegments(); int boundary = 0; for (int i = 0; i < segments.size(); i++) { LiveUpdateSegment seg = segments.get(i); style.addProgressSegment( new Notification.ProgressStyle.Segment(seg.getLength()).setColor(seg.getColor())); boundary += seg.getLength(); if (i < segments.size() - 1) { style.addProgressPoint(new Notification.ProgressStyle.Point(boundary)); } } return style; } } ``` * Kotlin ```kotlin import android.app.Notification import com.pushwoosh.liveupdates.LiveUpdateProgressStyleProvider import com.pushwoosh.liveupdates.LiveUpdateState class OrderStyleProvider : LiveUpdateProgressStyleProvider { override fun createStyle(state: LiveUpdateState): Notification.ProgressStyle { val style = Notification.ProgressStyle() state.progress?.let { style.setProgress(it) } style.setProgressIndeterminate(state.isProgressIndeterminate) val segments = state.segments var boundary = 0 segments.forEachIndexed { i, seg -> style.addProgressSegment( Notification.ProgressStyle.Segment(seg.length).setColor(seg.color)) boundary += seg.length if (i < segments.size - 1) { style.addProgressPoint(Notification.ProgressStyle.Point(boundary)) } } return style } } ``` Register the provider with a `` tag in **AndroidManifest.xml**. The class must have a public no-argument constructor. ```xml ``` Use `LiveUpdateState.getExtras()` to read the JSON you sent in `live_update.extras` and adapt the style to your own business data. ## Manage Live Updates from your app The server drives every `OPERATION_START`, `OPERATION_UPDATE`, and `OPERATION_END`, so there is no app-side API to post or refresh a Live Update. The `PushwooshLiveUpdates` facade covers only what the server cannot do — dismissing an update locally and checking which ones are on screen. ```java import com.pushwoosh.liveupdates.PushwooshLiveUpdates; // Dismiss a specific Live Update when the user finishes the activity in-app, // without waiting for the server's terminal "end" push PushwooshLiveUpdates.endLiveUpdate("order_4521"); // List the activity ids currently shown by this app List active = PushwooshLiveUpdates.getActiveIds(); // Clear everything this app is showing, for example on logout PushwooshLiveUpdates.endAllLiveUpdates(); ``` All methods are safe to call from any thread and are a no-op on devices below Android 16. ## Related links * [Pushwoosh Android SDK overview](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/) * [Pushwoosh Android SDK API reference](https://pushwoosh.github.io/pushwoosh-android-sdk/) * [Messaging API](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/) * [Android progress-centric notifications](https://developer.android.com/about/versions/16/features/progress-centric-notifications) --- # Migration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/migration-guide/ This guide refers to migration from the deprecated SDK versions to newer --- ### Migration from Android SDK 5 to 6 1. Migrate your project to AndroidX according to the [official documentation](https://developer.android.com/jetpack/androidx/migrate). 2. Add a new module in the app’s **build.gradle** file if you are using **Firebase Cloud Messaging.** build.gradle ```groovy implementation 'com.pushwoosh:pushwoosh-firebase:+' ``` For those who use **Amazon**, and do not use **Firebase**, no changes needed. ### Migration from previous versions of Android SDK Most of Pushwoosh classes and methods became deprecated since 5.0 release and some of them became extinct. **PushManager**, **BasePushMessageReceiver**, **BaseRegistrationReceiver**, **SendPushTagsCallback**, **PushFragment** and **PushEventListener** classes are still available as part of `com.pushwoosh:pushwoosh-deprecated` library but it is recommended to migrate to new API as soon as possible. ## PushManager **PushManager** consisted of various methods for different features. These methods are now split between different classes and libraries:\ `com.pushwoosh:pushwoosh`: **Pushwoosh**, **PushwooshNotificationSettings**, **PushwooshInApp**.\ `com.pushwoosh:pushwoosh-badge`: **PushwooshBadge**.\ `com.pushwoosh:pushwoosh-location`: **PushwooshLocation**.\ `com.pushwoosh:pushwoosh-beacon`: **PushwooshBeacon**. ## BaseRegistrationReceiver **BaseRegistrationReceiver** was used to handle push registration and unregistration events. This receiver is now replaced by simple callback mechanism: ```java Pushwoosh.getInstance().registerForPushNotifications(result -> { if (result.isSuccess()) { String token = result.getData(); // handle successful registration } else { PushwooshException exception = result.getException(); // handle registration error } }); ``` ## BasePushMessageReceiver **BasePushMessageReceiver** was used to imitate iOS notification behaviour when receiving push notification in foreground. It was achieved by cancelling incoming notification and invoking **onMessageReceive** callback. It was clunky and required manual registration when application becomes active and deregistration when application goes background.\ This receiver was replaced by **NotificationServiceExtension**: AndroidManifest.xml ```txt ``` YourNotificationServiceExtension.java ```java public class YourNotificationServiceExtension extends NotificationServiceExtension { @Override public boolean onMessageReceived(final PushMessage message) { if (isAppOnForeground()) { Handler mainHandler = new Handler(getApplicationContext().getMainLooper()); mainHandler.post(() -> { handlePush(message); }); // this indicates that notification should not be displayed return true; } return false; } @Override protected void startActivityForPushMessage(PushMessage message) { super.startActivityForPushMessage(message); handlePush(message); } @MainThread private void handlePush(PushMessage message) { // TODO: handle push message } } ``` This extension is also used to handle notification arrival and accept events thus replacing all clunky code that was used in manual Activity integration. ## PushFragment **PushFragment** was a lightweight alternative for the complex integration involving Activity lifecycle. But on the other hand it required **FragmentActivity** inheritance and implicitly used more complex Fragment lifecycle.\ **PushFragment** and **PushEventListener** are now replaced by **Pushwoosh#registerForPushNotifications(Callback)** and **NotificationServiceExtension**. ## Custom Push Broadcast Receiver (PW_NOTIFICATION_RECEIVER) **PW_NOTIFICATION_RECEIVER** was used to customise behaviour when user clicks on notification. This allowed to handle notifications outside of Activity context and to open different Activities depending on notification content. This integration used internal Pushwoosh SDK API which no longer exists.\ This receiver is now completely replaced by **NotificationServiceExtension**: YourNotificationServiceExtension.java ```java public class YourNotificationServiceExtension extends NotificationServiceExtension { @Override protected void startActivityForPushMessage(PushMessage message) { // super.startActivityForPushMessage() starts default launcher activity // or activity marked with ${applicationId}.MESSAGE action. // Simply do not call it to override this behaviour. // super.startActivityForPushMessage(message); // start your activity instead: Intent launchIntent = new Intent(getApplicationContext(), YourActivity.class); launchIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED); // (Optional) pass notification data to Activity launchIntent.putExtra(Pushwoosh.PUSH_RECEIVE_EVENT, message.toJson().toString()); context.startActivity(launchIntent); } } ``` ## Notification Factory There also were some breaking changes involving notification factory: **1.** **AbsNotificationFactory** was replaced by **NotificationFactory**\ **2.** **AbsNotificationFactory#onPushReceived(PushData)** and **AbsNotificationFactory#onPushHandle(Activity)** methods replaced by **NotificationServiceExtension** class (**onMessageReceived**, **startActivityForPushMessage**).\ **3.** **DefaultNotificationFactory** was replaced by **PushwooshNotificationFactory**.\ **4.** **PushData** was replaced by **PushMessage**. ## In-App Messages **1.** **InAppFacade** was replaced by **PushwooshInApp**.\ **2.** `pushwoosh` object was introduced for JavaScript native interface with following API:\ **getHwid(): string** - returns pushwoosh hwid for current device.\ **getVersion(): string** - returns current pushwoosh SDK version.\ **postEvent(event: string, attributes?: object, successCallback?: function, errorCallback?: function)** - sends postEvent request.\ **sendTags(tags: object)** - send tags associated with current device.\ **getTags(successCallback: function, errorCallback?: function)** - returns tags associated with current device.\ **closeInApp()** - closes In-App html page. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Notification channels **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/notification-channels/ How to create and use Notification Channels with Pushwoosh --- Starting with Pushwoosh SDK version **5.4.0**, you can use the Notification Channels functionality on **Android Oreo (API 26)** and higher. ## Creating a Notification Channel Our SDK handles notification channels out-of-the-box and creates them directly on users’ devices. To create a notification channel using Pushwoosh, here’s what you need to do: **1. Set up the channel’s configuration.**\ Specify the parameters such as sound, vibration, LED, or priority, and our SDK will set this configuration as channel’s default. Note that your users might change this configuration on their mobile phones. Note To learn the details of managing notification channels, please see [Android Developer guide](https://developer.android.com/guide/topics/ui/notifiers/notifications.html#ManageChannels). **2. Specify the channel’s name.**\ Add the following key-value pair to **Android root params** in your Control Panel: Android root params ```javascript {"pw_channel": "PUSH NOTIFICATION CHANNEL NAME"} ``` The first push received with a new channel’s name will create a channel with this name on users’ devices. Danger It is not possible to change the channel’s parameters after it’s been created on the device!\ To keep **the same** default channel parameters on all your users’ devices, you should use the same configuration whenever you send a push to a particular channel. Otherwise, your new users might have different default settings on their devices. To send a message to an existing channel, you need to pass the same key-value pair in **Android root params**, using the name of the existing channel. --- # VoIP calls on Android **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/pushwoosh-android-voip-calls/ Enable VoIP-style call notifications on Android using Pushwoosh. Integrate native call UI, manage calls, and customize behavior easily. --- Pushwoosh supports VoIP-style call notifications for Android using the `pushwoosh-calls` module. This enables native call UI and lifecycle management using Android’s telecom stack. ## 1. Add the `pushwoosh-calls` module Make sure to include the `pushwoosh-calls` dependency in your project. **Gradle:** ```groovy dependencies { implementation 'com.pushwoosh:pushwoosh-calls:' } ``` Replace `` with the current version from [Maven Central](https://mvnrepository.com/artifact/com.pushwoosh/pushwoosh-calls). ## 2. Request call permissions Request required permissions at runtime using the helper function: ```java import com.pushwoosh.calls.PushwooshCallSettings; PushwooshCallSettings.requestCallPermissions(); ``` > This handles requesting permissions like `READ_PHONE_NUMBERS`, necessary for proper call handling. ## 3. Implement `CallEventListener` and register it in the Manifest To handle incoming calls and events, implement the `CallEventListener` interface in your app. #### Java example ```java public class DemoCallEventListener implements CallEventListener { @Override public void onAnswer(@NonNull PushwooshVoIPMessage voIPMessage, int videoState) { // implement this method to navigate to your in-call UI Intent intent = new Intent(context, MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(intent); } @Override public void onReject(@NonNull PushwooshVoIPMessage voIPMessage) { // override this method to handle dismissed calls } @Override public void onDisconnect(@NonNull PushwooshVoIPMessage voIPMessage) { // override this method to handle disconnected calls } @Override public void onCreateIncomingConnection(@Nullable Bundle payload) { // this is the earliest point when call notification payload is accessible in your app, // use it to prepare your app UI for user interactions with call notifications } @Override public void onCallAdded(@NonNull PushwooshVoIPMessage voIPMessage) { // extension method of InCallService, provides customization options for paired Wearable devices } @Override public void onCallRemoved(@NonNull PushwooshVoIPMessage voIPMessage) { // extension method of InCallService, provides customization options for paired Wearable devices } } ``` #### AndroidManifest.xml Declare your listener class using a `` tag: ```xml ``` ## 4. Configure call settings (optional) Use `PushwooshCallSettings` to fine-tune call behavior, sounds, and display settings. #### Example usage ```kotlin import com.pushwoosh.calls.PushwooshCallSettings PushwooshCallSettings.setPhoneAccount("com.pushwoosh.voip") PushwooshCallSettings.setPhoneAccountHandle("PushwooshCallHandle") PushwooshCallSettings.setIncomingCallChannelName("Incoming Calls") PushwooshCallSettings.setOngoingCallChannelName("Ongoing Calls") PushwooshCallSettings.setCallSound("custom_ringtone.mp3") ``` ## Related links * [Pushwoosh Android SDK Overview](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/) * [Pushwoosh Call Plugin Source](https://github.com/Pushwoosh/pushwoosh-android-sdk) * [TelecomManager (Android Docs)](https://developer.android.com/reference/android/telecom/TelecomManager) --- # Setting up Pushwoosh Inbox UI Android **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/setting-up-pushwoosh-inbox-ui-android/ Add a Message Inbox screen to your Android app with rich cards, CTA buttons, and style customization via pushwoosh-inbox-ui. --- Pushwoosh Inbox UI ships a ready-made Android inbox screen (the “bell icon” App Inbox) on top of the Pushwoosh inbox backend. It renders messages in one of five card types, supports inline CTA buttons, and is open for style customization through XML attributes or code. ## Prerequisites * The [base Pushwoosh Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/) already integrated and sending pushes. * Kotlin support in your app module (`apply plugin: 'kotlin-android'`). ## Add the library Add the Kotlin plugin and the two Pushwoosh modules to your app’s `build.gradle`: build.gradle ```groovy apply plugin: 'kotlin-android' dependencies { implementation 'com.pushwoosh:pushwoosh-inbox:6.+' implementation 'com.pushwoosh:pushwoosh-inbox-ui:6.+' } ``` Pin `pushwoosh-inbox` and `pushwoosh-inbox-ui` to the same version as your existing `com.pushwoosh:pushwoosh` dependency. Replace `+` with the current version of [Pushwoosh Android SDK](https://github.com/Pushwoosh/pushwoosh-android-sdk/releases/latest). If your app uses ProGuard for code shrinking, keep the inbox plugin class: proguard-rules.pro ```proguard -keep public class com.pushwoosh.inbox.PushwooshInboxPlugin { *; } ``` ## Show the inbox Present the inbox as a standalone screen, or embed it as a fragment inside your own layout. As an activity: ```kotlin startActivity(Intent(this, InboxActivity::class.java)) ``` As a fragment: ```kotlin supportFragmentManager.beginTransaction() .replace(R.id.inboxContainer, PushwooshInboxUi.createInboxFragment()) .commitAllowingStateLoss() ``` ## Card types Inbox UI resolves a card type per message. The resolver reads `displayType` from the `data` object of the push payload, which the SDK delivers under `actionParams`. A `displayType` that names a known card type always renders that type, falling back to `classic` only if the type’s required fields are missing. This resolution does not depend on the heuristic setting below. When `displayType` is absent, the message renders as a plain row unless you opt in to the image/text heuristic: ```kotlin PushwooshInboxStyle.richCardsHeuristicEnabled = true ``` With the heuristic on, an image with no title renders `banner`, an image with title and body renders `captioned`, and anything else falls back to `classic`. | `displayType` | Appearance | Required payload field | Degrades to | | ------------- | ------------------------------------------------- | ----------------------------------------------------------- | -------------------------------------------------- | | `banner` | Full-bleed image, no text | image (message icon or `data.attachment`) | `classic` when no image | | `captioned` | Image on top, title + body below | image, message `title` and `content` | `classic` when the image, title or body is missing | | `classic` | Icon + title + body | — (title, body and icon expected) | — | | `carousel` | Swipeable multi-image gallery | message `title` and `content`, `data.carousel` (1–5 slides) | `classic` when no slides or no title/body | | `video` | Poster with play badge, full-screen player on tap | `data.video` (`url` + optional `poster`) | `classic` when no descriptor | The Apple Wallet card from the iOS InboxKit has no Android counterpart. A message with `displayType: "wallet"` always renders as `classic` on Android. ### Carousel card Slides live in `data.carousel`. Each slide needs an `image`. `title` (caption overlay) and `url` (opened on tap) are optional. A slide without an image is dropped, and at most 5 slides are shown. POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "content": "Swipe through this week's drops", "inbox_days": 7, "data": { "displayType": "carousel", "carousel": [ { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" }, { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" } ] }, "platforms": [3] }] } } ``` ### Video card The descriptor lives in `data.video`: `url` is required, `poster` is an optional preview image. Tapping the poster opens a full-screen player. POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "content": "Tap to play", "inbox_days": 7, "data": { "displayType": "video", "video": { "url": "https://cdn.example.com/inbox/clip.mp4", "poster": "https://cdn.example.com/inbox/poster.jpg" } }, "platforms": [3] }] } } ``` ## Read custom data from a message To make a push appear in the inbox, the [Messages API](https://docs.pushwoosh.com/developer/api-reference/messages-api/) `createMessage` request must include `inbox_image`, `inbox_date`, or `inbox_days`. Without one of those fields, the push is delivered as a regular notification and never reaches the inbox feed. Free-form custom data goes under `data`, which the SDK exposes as `actionParams` on `InboxMessage`: ```kotlin PushwooshInboxUi.onMessageClickListener = OnInboxMessageClickListener { message -> val params = message.actionParams?.let { JSONObject(it) } val promoId = params?.optString("promo_id") if (!promoId.isNullOrEmpty()) { navigateToPromo(promoId) } } ``` ## Add inline CTA buttons A message can carry inline call-to-action buttons inside `data.buttons`: POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "content": "Tap a button to claim or save", "inbox_image": "https://cdn.example.com/inbox/promo.png", "inbox_days": 7, "data": { "displayType": "captioned", "promo_id": "SUMMER2026", "buttons": [ { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" }, { "title": "Read", "action": "markRead" }, { "title": "Save", "action": "custom", "tag": "save_promo" } ] }, "platforms": [3] }] } } ``` Each button needs a `title`. Resolution follows this priority: * `action` set to `dismiss` or `markRead` (case-insensitive) runs that action. * Otherwise, a non-empty, parseable `url` resolves to an `openURL` action. * Otherwise, the tap resolves to a custom action, and every extra key on the button object is forwarded to your listener as its payload. Intercept taps from `PushwooshInboxUi.onButtonClickListener`. Return `true` to let the SDK perform the button’s default action, `false` to suppress it: ```kotlin PushwooshInboxUi.onButtonClickListener = OnInboxButtonClickListener { message, button -> when (val action = button.action) { is InboxCardButton.Action.OpenUrl -> true InboxCardButton.Action.Dismiss, InboxCardButton.Action.MarkRead -> true is InboxCardButton.Action.Custom -> { when (action.payload.optString("tag")) { "save_promo" -> saveCurrentPromoLocally(message) } true } } } ``` ## Customize the style Set colors, fonts, and empty/error states from code through `PushwooshInboxStyle`: ```kotlin PushwooshInboxStyle.accentColor = ContextCompat.getColor(this, R.color.brand_accent) PushwooshInboxStyle.titleColor = ContextCompat.getColor(this, R.color.brand_title) PushwooshInboxStyle.listEmptyText = "You have no messages yet" PushwooshInboxStyle.showToolbar = false ``` Or apply the same set of attributes as a theme, listed in [attrs.xml](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk/blob/master/InboxUiLibrary/pushwoosh-inbox-ui/src/main/res/values/attrs.xml): `inboxAccentColor`, `inboxTitleColor`, `inboxBackgroundColor`, `inboxDefaultIcon`, and the rest of the color and appearance attributes. Both approaches, plus a full sample app, are in the [pushwoosh-inbox-ui-android-sdk InboxSample](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk/tree/master/InboxSample) repo. ## Unread messages badge Note This is an in-app badge, for example on your own Inbox icon inside the app’s UI. It does not touch the app icon badge. No Pushwoosh SDK ties the unread count to the app icon. ```kotlin PushwooshInbox.unreadMessagesCount { result -> if (result.isSuccess) { val count = result.data } else { Log.e("App", "Failed to get unread count", result.exception) } } ``` --- # Using Pushwoosh location module in Android apps **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/using-pushwoosh-geolocation-module-in-android-apps/ Use the Pushwoosh location module to update the user’s location when your app is active or in the background --- Caution Please be aware that geo-based messaging is not available for HMS (Huawei Mobile Services) devices as it’s built upon Google location services. There are two location access categories in Android: * **Foreground** location access * **Background** location access For updating the user’s location in the **foreground**, one of the following conditions should be met: * One of the app’s activities is active * Foreground service of foregroundServiceType = “location” is launched for the app **Background** access allows the app to get the user’s geolocation while the app is inactive, and no foreground services of the “location” type are launched for that app. ## Foreground location access If you’re going to use the Pushwoosh location module to update user’s location only when your app is active, add the `pushwoosh-location` module to your **app/build.gradle**: build.gradle ```java implementation 'com.pushwoosh:pushwoosh-location:6.+' ``` Note Get the latest module version in Pushwoosh Github repo: To launch the module, use the `startLocationTracking` method: * Java ```java PushwooshLocation.startLocationTracking(); ``` * Kotlin ```kotlin PushwooshLocation.startLocationTracking() ``` Note The `pushwoosh-location` module will request all the necessary permissions from a user (ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION). ## Using ForegroundService to make location requests when your application is in background Deprecated Due to Android 12 updates, ForegroundService cannot perform correctly, so it’s deprecated and will be removed from Pushwoosh SDK further. Here are some workarounds for tracking users’ location in background: 1. Use targetSdkVersion 30 instead of 31. Please keep in mind that features present in Android 12 will not be available in that case. 2. Ask your users to disable battery optimization for your app to let the ForegroundService start in background. To launch the **ForegroundService** when the `startLocationTracking` method is called, which allows to get the user’s geolocation when your application is in the background, follow the steps described in the [previous paragraph](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/using-pushwoosh-geolocation-module-in-android-apps/#foreground-location-access), then add the following meta-data to your **AndroidManifest.xml**: ```txt ... .... ... ... ``` When **ForegroundService** is launched, a notification is being created. You can customize the notification by specifying the additional meta-data in your project’s **AndroidManifest.xml**: ```txt ... .... ... ... ``` ## Background location access For updating the geolocation in the background with no ForegroundService launching, you will need the following: * Get the approval from Google Play for your app’s access to location in the background (refer to for details) * Add the permission to your AndroidManifest.xml: ```txt ``` * Request user’s permission on updating location in the background after the foreground location permission is received (find the guidelines in the Android developer documentation: ) In the 6.3.2 `pushwoosh-location` module version, we included an additional method that opens the location permission settings screen if the following conditions are met: * There is the foreground location permission granted for the app; * `ACCESS_BACKGROUND_LOCATION` user-permission is present in the app’s AndroidManifest.xml. Caution Please make sure to clearly explain to users for what reasons and purposes your app needs access to their location in the background. Refer to the [Android developer guides](https://developer.android.com/training/location/permissions#background-dialog-target-android-11) for details. Call the method as follows: * Java ```java PushwooshLocation.requestBackgroundLocationPermission(); ``` * Kotlin ```plaintext PushwooshLocation.requestBackgroundLocationPermission() ``` ## Android 12+ permission types [Starting from Android 12](https://developer.android.com/about/versions/12/approximate-location), when being asked for foreground location access permission, a user can choose how often and with what precision the app will get the user’s location. There are two types of that permissions: 1. Approximate location, that is equal to ACCESS_COARSE_LOCATION 2. Precise location, that is equal to ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION Caution Please take into account that location requests from applications with Manifest.permission.ACCESS_COARSE_LOCATION and not Manifest.permission.ACCESS_FINE_LOCATION will be automatically throttled to a slower interval, and the location object will be obfuscated to only show a coarse level of accuracy. Refer to[ https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest](https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest) for details. Note Keep in mind that the level of precision depends on which permission you request: * ACCESS_COARSE_LOCATION – Provides an estimate of the device’s location, to within about 1 mile (1.6 km). * ACCESS_FINE_LOCATION – Provides an estimate of the device’s location that is as accurate as possible, usually within about 160 feet (50 meters) and sometimes as accurate as within 10 feet (a few meters) or better. Source: ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Managing user consent **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/consent-management/ Manage when the SDK initiates and collects data by requiring user consent before starting communication. --- Pushwoosh supports compliance with privacy regulations (e.g., GDPR, CCPA) by allowing developers to control when the SDK begins communication with Pushwoosh servers. This helps ensure that no data is collected before the user gives explicit consent. ### Overview By default, the Pushwoosh SDK begins communication and collecting device data immediately upon initialization. However, you can change this behavior so that no communication happens until the user gives consent. With this setup: * If the user gives consent, the SDK initializes and begins collecting data. * If the user does not give consent, the SDK stays inactive and does not collect any data. * If the user later withdraws consent, the SDK stops all activity and disconnects from the servers. Pushwoosh provides mechanisms to manage this flow in iOS, Android, and Unity. ### iOS #### Disable SDK server communication at startup By default, the communication with the SDK is enabled. To disable all communication with Pushwoosh servers until consent is explicitly granted by the user, add the following key to your **`Info.plist`**: ```xml Pushwoosh_ALLOW_SERVER_COMMUNICATION ``` #### Check communication status Use the following methods to determine if communication is currently allowed: * SwiftUI ```swift import SwiftUI import PushwooshFramework var serverCommunicationAllowed = PWCoreServerCommunicationManager.sharedInstance.isServerCommunicationAllowed print("isServerCommunicationAllowed: ", serverCommunicationAllowed) ``` * Swift ```swift import PushwooshFramework var serverCommunicationAllowed = PWCoreServerCommunicationManager.sharedInstance.isServerCommunicationAllowed print("isServerCommunicationAllowed: ", serverCommunicationAllowed) ``` * Objective-C ```objective-c #import BOOL serverCommunicationAllowed = [PWCoreServerCommunicationManager sharedInstance].isServerCommunicationAllowed; NSLog(@"isServerCommunicationAllowed: %d", serverCommunicationAllowed); ``` #### Enable SDK communication after consent Once the user has granted consent, enable communication as follows: * SwiftUI ```swift import SwiftUI import PushwooshFramework Pushwoosh.sharedInstance().startServerCommunication() ``` * Swift ```swift import PushwooshFramework Pushwoosh.sharedInstance().startServerCommunication() ``` * Objective-C ```objective-c #import [[Pushwoosh sharedInstance] startServerCommunication]; ``` #### Register for push notifications Once communication is enabled, explicitly register for push notifications: * SwiftUI ```swift import SwiftUI import PushwooshFramework Pushwoosh.sharedInstance().registerForPushNotifications() ``` * Swift ```swift import PushwooshFramework Pushwoosh.sharedInstance().registerForPushNotifications() ``` * Objective-C ```objective-c #import [[Pushwoosh sharedInstance] registerForPushNotifications]; ``` #### Disable communication To stop communication with Pushwoosh servers (e.g., if the user revokes consent): * SwiftUI ```swift import SwiftUI import PushwooshFramework Pushwoosh.sharedInstance().stopServerCommunication() ``` * Swift ```swift import PushwooshFramework Pushwoosh.sharedInstance().stopServerCommunication() ``` * Objective-C ```objective-c #import [[Pushwoosh sharedInstance] stopServerCommunication]; ``` ### Android #### Disable SDK server communication at startup By default, communication is enabled. To prevent any data from being sent to Pushwoosh servers until user consent is obtained, add the following to your `AndroidManifest.xml`: ```xml ``` #### Check communication status Use the following methods to determine if server communication is currently allowed: * Java ```java import com.pushwoosh.Pushwoosh; boolean isCommunicationEnabled = Pushwoosh.getInstance().isServerCommunicationAllowed(); Log.d("Pushwoosh", "Communication enabled = " + isCommunicationEnabled); ``` * Kotlin ```kotlin import com.pushwoosh.Pushwoosh val isCommunicationEnabled = Pushwoosh.getInstance().isServerCommunicationAllowed() Log.d("Pushwoosh", "Communication enabled = $isCommunicationEnabled") ``` #### Enable SDK communication after consent Once the user has granted consent, enable communication as follows: * Java ```java import com.pushwoosh.Pushwoosh; Pushwoosh.getInstance().startServerCommunication(); ``` * Kotlin ```kotlin import com.pushwoosh.Pushwoosh Pushwoosh.getInstance().startServerCommunication() ``` #### Register for push notifications After communication is enabled, explicitly register for push notifications: * Java ```java import com.pushwoosh.Pushwoosh; Pushwoosh.getInstance().registerForPushNotifications(); ``` * Kotlin ```kotlin import com.pushwoosh.Pushwoosh Pushwoosh.getInstance().registerForPushNotifications() ``` #### Disable communication To stop communication with Pushwoosh servers (e.g., if the user revokes consent): * Java ```java import com.pushwoosh.Pushwoosh; Pushwoosh.getInstance().stopServerCommunication(); ``` * Kotlin ```kotlin import com.pushwoosh.Pushwoosh Pushwoosh.getInstance().stopServerCommunication() ``` ### Unity #### Disable SDK server communication at startup By default, the communication with the SDK is enabled. To disable all communication with Pushwoosh servers until consent is explicitly granted by the user, apply platform-specific settings: **Android** Add the following to your Unity project at `AndroidManifest.xml`: ```xml ``` **iOS** Modify `Info.plist`: ```xml Pushwoosh_ALLOW_SERVER_COMMUNICATION ``` **Note:** You must enable communication before calling `RegisterForPushNotifications`. #### Check communication status Use the following method to check whether server communication is currently allowed: ```c# bool enabled = Pushwoosh.Instance.IsCommunicationEnabled(); ``` #### Enable SDK communication after consent To enable communication after consent: ```c# Pushwoosh.Instance.SetCommunicationEnabled(true); ``` #### Register for push notifications Once communication is enabled, you can register the device for push notifications: ```c# Pushwoosh.Instance.RegisterForPushNotifications(); ``` #### Disable communication To stop communication with Pushwoosh servers (e.g., if the user revokes consent): ```c# Pushwoosh.Instance.SetCommunicationEnabled(false); ``` ### Web Push #### Disable automatic subscription at startup By default, the Pushwoosh SDK shows the native subscription prompt as soon as it is initialized. To prevent the SDK from automatically showing the subscription prompt upon initialization, set the `communicationEnabled` parameter to `false` in the `init` call. ```html ``` #### Enable subscription after consent Once you have disabled automatic subscription, you can prompt the user to subscribe at any time. When the user agrees to receive push notifications (e.g., by clicking a “Subscribe” button on your custom UI), you can enable communication by calling the `setCommunicationEnabled` method. Calling `Pushwoosh.setCommunicationEnabled(true)` enables communication with Pushwoosh services. Once enabled, the SDK will proceed to show the native browser permission prompt. ```javascript Pushwoosh.setCommunicationEnabled(true) .then(() => { console.log('User is subscribed to push notifications.'); }) .catch((error) => { console.error('Error subscribing user:', error); }); ``` #### Disable communication To stop communication with Pushwoosh services (e.g., if the user revokes consent), call `setCommunicationEnabled` with `false`. ```javascript Pushwoosh.setCommunicationEnabled(false); ``` --- # Cross-platform frameworks guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/ Learn how to integrate Pushwoosh SDK into various cross-platform frameworks. --- Welcome to the cross-platform frameworks integration guide for implementing Pushwoosh SDK in your project. This section provides detailed instructions and resources to help you integrate Pushwoosh SDK into various cross-platform frameworks. Select your preferred framework to get started. ### Capacitor Learn how to set up Pushwoosh SDK in your Capacitor project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/) ### Cordova Discover how to set up Pushwoosh SDK in your Cordova project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/) ### Flutter Get started with Pushwoosh SDK integration in your Flutter project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/) ### Mendix Add push notifications to your Mendix native mobile app with the Pushwoosh module. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/) ### Outsystems Instructions to integrate Pushwoosh SDK into your Outsystems application. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/) ### Expo Learn how to set up Pushwoosh SDK in an Expo-managed project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/expo/integrating-expo-plugin/) ### React Native Detailed guidance on integrating Pushwoosh SDK into React Native projects. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/) ### Unity Set up Pushwoosh SDK in your Unity project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/) ### MAUI .NET Step-by-step guide to integrating Pushwoosh SDK with your MAUI .NET project. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/) --- # Capacitor Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/ Explore the official Pushwoosh Capacitor SDK documentation. Find integration guides, API reference, configuration options, examples & support to add push notifications to your Capacitor app --- Choose the integration method that best suits your needs to get started with the Pushwoosh Capacitor SDK: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/integration/quick-start) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/integration/basic-integration-guide/) - a step-by-step approach to setting up the Capacitor SDK from scratch --- # Capacitor SDK Basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/integration/basic-integration-guide/ Learn how to integrate the Pushwoosh Capacitor SDK, configure users, track events, set tags and handle push notifications on iOS and Android --- This section contains information on how to integrate the Pushwoosh Capacitor SDK into your application. ## Prerequisites To integrate the Pushwoosh Capacitor SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * To add the plugin with Swift Package Manager: Capacitor 7 or 8. To migrate a CocoaPods project to Swift Package Manager: `@capacitor/cli` 7.4.0 or later. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Add Pushwoosh Capacitor SDK Dependency Add the Pushwoosh Capacitor SDK dependency to your project: ```bash npm install pushwoosh-capacitor-plugin ``` Sync the Capacitor configuration: ```bash npx cap sync ``` #### 1.1 Choose the iOS package manager `npx cap sync` installs the iOS part of the plugin with the package manager your Capacitor project uses: Swift Package Manager or CocoaPods. Both ship the same native Pushwoosh SDK version and Inbox UI. Swift Package Manager is supported with Capacitor 7 and Capacitor 8. An existing project keeps its package manager: if `ios/App/CapApp-SPM` exists, the Capacitor CLI uses Swift Package Manager, otherwise CocoaPods. For a new iOS platform: * Capacitor 8 Capacitor 8 creates a Swift Package Manager project by default: ```bash npx cap add ios ``` To use CocoaPods instead, run `npx cap add ios --packagemanager CocoaPods`. * Capacitor 7 Capacitor 7 creates a CocoaPods project by default. To use Swift Package Manager, run: ```bash npx cap add ios --packagemanager SPM ``` The command creates the project and then stops with an `ENOENT ... Podfile` error while trying to run `pod install`. The project is created correctly. Finish the setup with: ```bash npx cap sync ios ``` With Swift Package Manager, the Capacitor CLI adds the plugin to `ios/App/CapApp-SPM`, and the plugin brings in the Pushwoosh iOS SDK and Inbox UI. Don’t add the Pushwoosh SDK to the app a second time, as a pod or as a package with a different version. CocoaPods deprecation CocoaPods trunk becomes read-only on December 2, 2026, and Pushwoosh moves the iOS SDK to Swift Package Manager. Plugin releases after that date will use the Swift package only. Until then, `pod install` and the Xcode build of a CocoaPods project show a warning about it. The warning doesn’t affect the build. To migrate an existing CocoaPods project, run `npx cap spm-migration-assistant`, then follow the [manual Xcode step](https://capacitorjs.com/docs/ios/spm#using-our-migration-tool) it links to. The command needs `@capacitor/cli` 7.4.0 or later. ### 2. Capacitor SDK Initialization In your main `JavaScript` file, import and initialize the Pushwoosh SDK: index.js ```javascript import { Pushwoosh } from 'pushwoosh-capacitor-plugin'; // Initialize the SDK Pushwoosh.onDeviceReady({ appid: "__YOUR_APP_CODE__" }); // Register for push notifications Pushwoosh.registerDevice() .then(result => { console.log("Push token:", result.pushToken); // Handle successful registration }) .catch(error => { console.error("Failed to register device:", error); // Handle registration error }); ``` Where: * `__YOUR_APP_CODE__` is the application code from the Pushwoosh Control Panel. ### 3. iOS Native Setup #### 3.1 Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. #### 3.2 Info.plist In your `ios/App/App/Info.plist` set the `__PUSHWOOSH_DEVICE_API_TOKEN__` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): info.plist ```xml Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` #### 3.3 Message delivery tracking You must add a Notification Service Extension target to your project. This is essential for accurate delivery tracking and features like Rich Media on iOS. Follow the [native guide’s steps](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target and the necessary Pushwoosh code within it. Add the Pushwoosh SDK to the extension target according to your package manager: * **CocoaPods:** follow the native guide’s **Dependencies for the Notification Service Extension** step. In `ios/App/Podfile`, add `pod 'PushwooshXCFramework'` without a version to the extension target, so it resolves to the version the plugin uses, then run `npx cap sync ios`. * **Swift Package Manager:** skip the native guide’s CocoaPods dependencies step. Add the SDK to the extension target in Xcode yourself, because the target doesn’t inherit the plugin’s dependencies: 1. Open the extension target > **General** > **Frameworks and Libraries**. 2. Add the `PushwooshFramework` product of the `Pushwoosh-XCFramework` package. Use the version already in `Package.resolved`. If you migrated from CocoaPods, make sure the extension no longer gets `PushwooshXCFramework` as a pod, so the app doesn’t include the SDK twice. Then `import PushwooshFramework` in the extension code. The App target needs nothing extra, as it gets the Pushwoosh SDK through the plugin. ### 4. Android Native Setup #### 4.1 Install dependencies Ensure that the required dependencies and plugins are added to your Gradle scripts: Add the Google Services Gradle plugin to your project-level `build.gradle` dependencies: android/build.gradle ```groovy buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' } } ``` Apply the plugin in your app-level `build.gradle` file: app/build.gradle ```groovy apply plugin: 'com.google.gms.google-services' ``` #### 4.2 Add Firebase configuration file Place the `google-services.json` file into `android/app` folder in your project directory. #### 4.3 Add Pushwoosh metadata In your `main/AndroidManifest.xml` add [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) inside the `` tag: AndroidManifest.xml ```xml ``` > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 5. Run the Project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notification event listeners In the Pushwoosh Capacitor SDK there are two callback methods for handling push notifications: * `pushReceivedCallback` is triggered when a push notification is received * `pushOpenedCallback` is triggered when a user opens a notification You should set up these callbacks right after initialization of the SDK: index.js ```javascript import { Pushwoosh } from 'pushwoosh-capacitor-plugin'; // Set up push received callback await Pushwoosh.pushReceivedCallback((notification, err) => { if (err) { console.error("Failed to process received notification:", err); } else { console.log("Push received:", JSON.stringify(notification)); // Handle the received notification } }); // Set up push opened callback await Pushwoosh.pushOpenedCallback((notification, err) => { if (err) { console.error("Failed to process opened notification:", err); } else { console.log("Push opened:", JSON.stringify(notification)); // Handle the opened notification } }); ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty ```javascript import { Pushwoosh } from 'pushwoosh-capacitor-plugin'; class Registration { async afterUserLogin(user) { // Set user ID Pushwoosh.setUserId(user.getId); // Setting additional user information as tags for Pushwoosh await Pushwoosh.setTags({ "age": user.getAge(), "name": user.getName(), "last_login": user.getLastLoginDate() }); } } ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. ```javascript import { Pushwoosh } from 'pushwoosh-capacitor-plugin'; class UpdateUser { async afterUserUpdateProfile(user) { // Set list of favorite categories await Pushwoosh.setTags({ "favorite_categories": user.getFavoriteCategoriesList() }); // Set payment information await Pushwoosh.setTags({ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() }); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions ```javascript import { Pushwoosh } from 'pushwoosh-capacitor-plugin'; class Registration { // Track login event afterUserLogin(user) { Pushwoosh.postEvent("login", { "name": user.getName(), "last_login": user.getLastLoginDate() }); } // Track purchase event afterUserPurchase(product) { Pushwoosh.postEvent("purchase", { "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() }); } } ``` ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # Capacitor SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/integration/quick-start/ Get started with the Pushwoosh Capacitor SDK using this quick and simple example. --- Get started with the Pushwoosh Capacitor SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh Capacitor SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project Clone the SDK repository: ```bash git clone https://github.com/Pushwoosh/pushwoosh-capacitor-plugin.git ``` Navigate to the plugin directory: ```bash cd pushwoosh-capacitor-plugin ``` ## Build the plugin Before running the example app, you need to build the Capacitor plugin.\ Install plugin dependencies: ```bash npm i ``` Build the plugin: ```bash npm run build ``` ## Configure the example app Navigate to the example app directory: ```bash cd example ``` Install the app dependencies: ```bash npm i ``` Build the web assets: ```bash npm run build ``` Open **src/js/example.js** and update the `Pushwoosh.onDeviceReady` call: ```javascript Pushwoosh.onDeviceReady({ appid: "__YOUR_APP_CODE__" }); ``` Where: `__YOUR_APP_CODE__` is the application code from the Pushwoosh Control Panel. ### iOS ###### 1. Install CocoaPods dependencies The example app is a Capacitor 7 project that uses CocoaPods. Navigate to the iOS platform directory and install the CocoaPods dependencies: ```bash cd ios/App pod install --repo-update cd ../.. ``` If you migrated the example app to Swift Package Manager, skip this step. `npx cap sync ios` in the next step adds the plugin. CocoaPods deprecation Plugin releases after December 2, 2026 will support Swift Package Manager only, so `pod install` and the Xcode build of this example show a warning. To set up your own app with Swift Package Manager or migrate the example, see [Choose the iOS package manager](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/capacitor/integration/basic-integration-guide/#11-choose-the-ios-package-manager). ###### 2. Sync Capacitor to iOS Sync the Capacitor configuration: ```bash npx cap sync ios ``` ###### 3. Open the iOS project Open the project in Xcode: ```bash npx cap open ios ``` ###### 4. Set the Bundle Identifier In Xcode, set the **Bundle Identifier** to your app’s package name (e.g., `com.example.pushwoosh`). ###### 5. Add capabilities To enable Push Notifications, you need to add certain capabilities. In the `Signing & Capabilities` section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. ###### 6. Set the API Token Open `ios/App/App/Info.plist` in Xcode and add the following entry with your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): Info.plist ```xml Pushwoosh_API_TOKEN __YOUR_PUSHWOOSH_DEVICE_API_TOKEN__ ``` ### Android ###### 1. Sync Capacitor to Android Sync the Capacitor configuration to the Android platform: ```bash npx cap sync android ``` ###### 2. Setup google-services.json Copy your `google-services.json` file to the **android/app** directory. ###### 3. Set the package name Open **android/app/build.gradle** and set the **applicationId** to your app’s package name (e.g., `com.example.pushwoosh`). ###### 4. Set the API Token Open your **android/app/src/main/AndroidManifest.xml** file and add the following metadata inside the `` tag: AndroidManifest.xml ```xml ``` Replace `__YOUR_PUSHWOOSH_DEVICE_API_TOKEN__` with your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). ###### 5. Open the Android project Open the project in Android Studio: ```bash npx cap open android ``` ## Run the project 1. Build and run the project. 2. Tap the **“Click Me!”** button. 3. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Cordova Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/ Explore the official Pushwoosh Cordova SDK documentation with integration guides, API reference, configuration options, examples and support --- Choose the integration method that best suits your needs to get started with the Pushwoosh Cordova SDK: * [AI-Assisted Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/ai-integration/) - use AI coding assistants to integrate and configure the Pushwoosh Cordova plugin Or doing by yourself: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/quick-start) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/basic-integration-guide/) - a step-by-step approach to setting up the Cordova SDK from scratch * [Integrating the Cordova Huawei plugin](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/integrating-cordova-huawei-plugin/) - learn how to enable push notification support specifically for Huawei devices using the Huawei plugin --- # AI-Assisted Integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/ai-integration/ Integrate Pushwoosh Cordova plugin using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP --- Use AI coding assistants to integrate and configure the Pushwoosh Cordova plugin in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date plugin documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh Cordova plugin docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh Cordova plugin library ID on Context7: **`/pushwoosh/pushwoosh-phonegap-plugin`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the plugin. Below are ready-to-use prompts for common tasks. ### Basic plugin integration ```text Integrate Pushwoosh Cordova plugin into my Cordova project. Requirements: - Install pushwoosh-cordova-plugin via cordova plugin add - Initialize Pushwoosh with my App ID in onDeviceReady - Register for push notifications and handle push-receive and push-notification events Use Context7 MCP to fetch Pushwoosh Cordova plugin documentation. ``` ### Push notification handling ```text Show me how to handle push notification callbacks (receive, open) with Pushwoosh Cordova plugin. I want to process push payload data and navigate to specific pages based on custom data. Use Context7 MCP to fetch Pushwoosh Cordova plugin documentation for push event handling. ``` ### User segmentation with tags ```text Show me how to use Pushwoosh tags for user segmentation in a Cordova app. I need to set tags, get tags, and set user ID for cross-device tracking. Use Context7 MCP to fetch Pushwoosh Cordova plugin documentation for setTags and getTags. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh Cordova plugin documentation”** at the end of your prompt tells the AI assistant to pull the latest plugin docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh Cordova plugin, it receives: * **Installation instructions** — plugin installation and platform-specific setup * **API reference** — public methods, events, and callback signatures * **Code examples** — JavaScript snippets for common use cases * **Event documentation** — push-receive, push-notification, and other event handlers * **Configuration details** — plugin variables, platform-specific settings, and permissions --- # Custom call event handler on Android **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/android-custom-call-event-handler/ Handle Android VoIP call events in your own Java class with the Pushwoosh Cordova plugin, for example to open a custom call screen. --- A custom call event handler is your own Java class that receives [VoIP call](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/voip-cordova/) events on the native Android side. Use it to open your own call screen when the user answers, or to pass call events to a part of the app that the main WebView can’t reach, such as a second activity with its own WebView. ## How the handler works * For every call event, the plugin first sends the JavaScript event to the main WebView, then calls your handler. Your `registerEvent` listeners keep working. * When a handler is set, the plugin doesn’t bring the app to the foreground on Answer. Without a handler, the plugin opens the app’s launcher activity. With a handler, open your screen in `onAnswer`, or the app stays in the background. * The plugin reads the class name from the app manifest on the first call event after the app process starts, and creates the class through reflection. The class needs a public constructor without arguments. * The callbacks don’t receive a `Context`. Keep an application `Context` in your app, for example in your `Application` subclass, as `MyApp.context()` does in the example below. ### Call events The handler implements `com.pushwoosh.calls.listener.CallEventListener`. Implement all eight methods, and leave the ones you don’t need empty. | Method | Called when | JavaScript event | | -------------------------------------------------------- | ------------------------------------------- | ------------------------- | | `onCreateIncomingConnection(Bundle payload)` | An incoming call starts ringing | `voipPushPayload` | | `onAnswer(PushwooshVoIPMessage message, int videoState)` | The user answers | `answer` | | `onReject(PushwooshVoIPMessage message)` | The user declines | `reject` | | `onDisconnect(PushwooshVoIPMessage message)` | The call ends | `hangup` | | `onCallCancelled(PushwooshVoIPMessage message)` | A cancel push cancels the call | `voipDidCancelCall` | | `onCallCancellationFailed(String callId, String reason)` | A cancel push can’t be applied | `voipDidFailToCancelCall` | | `onCallAdded(PushwooshVoIPMessage message)` | The system in-call service adds the call | None | | `onCallRemoved(PushwooshVoIPMessage message)` | The system in-call service removes the call | None | `PushwooshVoIPMessage` gives you the values from the VoIP push: | Getter | Returns | | ---------------------- | ------------------------------------------------------ | | `getCallId()` | `callId`, or `null` if the push has none | | `getCallerName()` | `callerName`, or `Unknown Caller` if the push has none | | `getHasVideo()` | `video` | | `getSupportsHolding()` | `supportsHolding` | | `getSupportsDTMF()` | `supportsDTMF` | | `getCancelCall()` | `cancelCall` | | `getRawPayload()` | The whole push `Bundle`, including your custom keys | `onCreateIncomingConnection` receives the raw `Bundle`. To use the getters there, wrap it: `new PushwooshVoIPMessage(payload)`. ## 1. Register the handler in the manifest Add a `meta-data` entry to `` with the full class name of your handler: ```xml ``` [Step 3](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/android-custom-call-event-handler/#3-add-the-code-to-your-project) shows where this entry goes in Cordova and in Capacitor. ## 2. Write the handler This handler opens your call screen on Answer and sends each call event as a broadcast inside the app: ```java public class MyCallEventHandler implements CallEventListener { public static final String ACTION_CALL_EVENT = "com.example.app.CALL_EVENT"; /** * Called when the user answers the call. * Opens the app on the call screen and broadcasts the event to the rest of the app. */ @Override public void onAnswer(@NonNull PushwooshVoIPMessage message, int videoState) { Context context = MyApp.context(); // your application Context Intent intent = new Intent(context, VideoCallActivity.class) .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP) .putExtra("callId", message.getCallId()); context.startActivity(intent); broadcast("answer", message); } @Override public void onReject(@NonNull PushwooshVoIPMessage message) { broadcast("reject", message); } @Override public void onDisconnect(@NonNull PushwooshVoIPMessage message) { broadcast("hangup", message); } @Override public void onCallCancelled(@NonNull PushwooshVoIPMessage message) { broadcast("cancelled", message); } @Override public void onCreateIncomingConnection(@Nullable Bundle payload) {} @Override public void onCallAdded(@NonNull PushwooshVoIPMessage message) {} @Override public void onCallRemoved(@NonNull PushwooshVoIPMessage message) {} @Override public void onCallCancellationFailed(@Nullable String callId, @NonNull String reason) {} private static void broadcast(String event, PushwooshVoIPMessage message) { Context context = MyApp.context(); // your application Context Intent intent = new Intent(ACTION_CALL_EVENT) .setPackage(context.getPackageName()) .putExtra("event", event) .putExtra("callId", message.getCallId()); context.sendBroadcast(intent); } } ``` ## 3. Add the code to your project ### Capacitor Add the handler class to `android/app/src/main/java/` and the `meta-data` entry from Step 1 to `android/app/src/main/AndroidManifest.xml`. Add the Pushwoosh calls SDK to the app module in `android/app/build.gradle`. Use the version set in the plugin’s `src/android/add-android-voip.gradle`: ```gradle dependencies { compileOnly "com.pushwoosh:pushwoosh-calls:6.17.1" } ``` Without it, the build fails with `package com.pushwoosh.calls.listener does not exist`, because the plugin adds the calls SDK only to its own module. ### Cordova Cordova regenerates the native project, so add the handler through a local plugin. Its `plugin.xml` copies the class and adds the `meta-data` to the manifest: ```xml ``` Cordova doesn’t need an extra Gradle dependency. ## Related links * [Cordova VoIP calls](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/voip-cordova/) * [Android VoIP calls](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/pushwoosh-android-voip-calls/) --- # Cordova plugin API reference **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/cordova-plugin-api-reference/ Complete API reference for Pushwoosh Cordova plugin including methods to manage push notifications, user data, tags and events --- example ```txt var pushwoosh = cordova.require("pushwoosh-cordova-plugin.PushNotification"); // Should be called before pushwoosh.onDeviceReady document.addEventListener('push-notification', function(event) { var notification = event.notification; // handle push open here }); // Initialize Pushwoosh. This will trigger all pending push notifications on start. pushwoosh.onDeviceReady({ appid: "XXXXX-XXXXX", serviceName: "XXXX" }); pushwoosh.registerDevice( function(status) { var pushToken = status.pushToken; // handle successful registration here }, function(status) { // handle registration error here } ); ``` ## onDeviceReady ```javascript PushNotification.prototype.onDeviceReady = function( config ) ``` *[android, ios, wp8, windows]*\ Initializes Pushwoosh plugin and triggers a start push message. Should be called on every app launch. `config.appid` – Pushwoosh application code. `config.serviceName` – MPNS service name for wp8 platform. example ```txt // initialize Pushwoosh with appid : "PUSHWOOSH_APP_ID", serviceName : "WINDOWS_PHONE_SERVICE". This will trigger all pending push notifications on start. pushwoosh.onDeviceReady({ appid : "XXXXX-XXXXX", serviceName: "XXXX" }); ``` ## registerDevice ```javascript PushNotification.prototype.registerDevice = function( success, fail ) ``` *[android, ios, wp8, windows]*\ Registers device for push notifications and retrieves a push Token. `success` – success callback. Push token is passed as “status.pushToken” parameter to this callback `fail` – error callback example ```txt pushwoosh.registerDevice( function(status) { alert("Registered with push token: " + status.pushToken); }, function(error) { alert("Failed to register: " + error); } ); ``` ## unregisterDevice ```javascript PushNotification.prototype.unregisterDevice = function( success, fail ) ``` *[android, ios, wp8, windows]*\ Unregisters device from receiving push notifications. `success` – success callback `fail` – error callback ## setTags ```javascript PushNotification.prototype.setTags = function( config, success, fail ) ``` *[android, ios, wp8, windows]*\ Sets tags for the device. **Parameters** `config` – object with custom device tags `success` – success callback. Push token is passed as “status.pushToken” parameter to this callback `fail` – error callback example ```txt // sets tags: “deviceName” with value “hello” and “deviceId” with value 10 pushwoosh.setTags({deviceName:"hello", deviceId:10}, function() { console.warn('setTags success'); }, function(error) { console.warn('setTags failed'); } ); // sets list tags "MyTag" with values (array) "hello", "world" pushwoosh.setTags({"MyTag":["hello", "world"]}); ``` ## getTags ```javascript PushNotification.prototype.getTags = function( success, fail ) ``` *[android, ios, wp8, windows]*\ Returns tags for the device including default tags. `success` – success callback. Receives tags as parameters `fail` – error callback ```javascript pushwoosh.getTags( function(tags) { console.warn('tags for the device: ' + JSON.stringify(tags)); }, function(error) { console.warn('get tags error: ' + JSON.stringify(error)); } ); ``` ## getPushToken ```javascript PushNotification.prototype.getPushToken = function( success ) ``` *[android, ios, wp8, windows]*\ Returns push token if it is available. Note that the token also comes in registerDevice function callback. `success` – success callback. ```javascript pushwoosh.getPushToken( function(token) { console.warn('push token: ' + token); } ); ``` ## getPushwooshHWID ```javascript PushNotification.prototype.getPushwooshHWID = function( success ) ``` *[android, ios, wp8, windows]*\ Returns Pushwoosh HWID used for communications with Pushwoosh API. `success` – getPushwooshHWID callback ```plaintext pushwoosh.getPushwooshHWID( function(token) { console.warn('Pushwoosh HWID: ' + token); } ); ``` ## getRemoteNotificationStatus ```javascript PushNotification.prototype.getRemoteNotificationStatus = function( callback, error ) ``` *[android, ios]*\ Returns detailed status of push notification permissions. `callback` – success callback. Receives object with following properties: ```plaintext { "enabled" : notificaions enabled flag. "pushBadge" : badges permission granted. (iOS only) "pushAlert" : alert permission granted. (iOS only) "pushSound" : sound permission granted. (iOS only) } ``` `error` — error callback ## setApplicationIconBadgeNumber ```javascript PushNotification.prototype.setApplicationIconBadgeNumber = function( badgeNumber ) ``` *[android, ios]*\ Set the application icon badge number. `badgeNumber` – icon badge number ## getApplicationIconBadgeNumber ```javascript PushNotification.prototype.getApplicationIconBadgeNumber = function( callback ) ``` *[android, ios]*\ Returns application icon badge number. `callback` – success callback ```plaintext pushwoosh.getApplicationIconBadgeNumber(function(badge){ alert(badge);} ); ``` ## addToApplicationIconBadgeNumber ```javascript PushNotification.prototype.addToApplicationIconBadgeNumber = function( badgeNumber ) ``` *[android, ios]*\ Adds value to application icon badge. `badgeNumber` — incremental icon badge number ## getLaunchNotification ```javascript PushNotification.prototype.getLaunchNotification = function( callback ) ``` *[android, ios]*\ Returns push notification payload if the app was started in response to a push notification, or null. `callback` – success callback ## clearLaunchNotification ```javascript PushNotification.prototype.clearLaunchNotification = function( callback ) ``` *[android, ios]*\ Clears launch notification, getLaunchNotification() will return null after this call. ## setUserId ```javascript PushNotification.prototype.setUserId = function( userId ) ``` *[android, ios]*\ Sets User indentifier– a Facebook ID, username,email, or any other user ID. This allows data and events to be matched across multiple user devices. `userId` – user string identifier ## postEvent ```javascript PushNotification.prototype.postEvent = function( event, attributes ) ``` *[android, ios]*\ Posts events for In-App Messages. This can trigger In-App message display as specified in Pushwoosh Control Panel. `event` – event to trigger `attributes` – object with additional event attributes ```plaintext pushwoosh.setUserId("XXXXXX"); pushwoosh.postEvent("buttonPressed", { "buttonNumber" : 4, "buttonLabel" : "banner" }); ``` ## createLocalNotification ```javascript PushNotification.prototype.createLocalNotification = function( config, success, fail ) ``` *[android, ios]*\ Schedules local notification. `config.msg` – notification message `config.seconds` – notification delay in seconds `config.userData` – additional data to pass in notification `success` – success callback `fail` – error callback ```plaintext pushwoosh.createLocalNotification({msg:"Your pumpkins are ready!", seconds:30, userData:{}}) ``` ## clearLocalNotification ```javascript PushNotification.prototype.clearLocalNotification = function() ``` *[android]*\ Clears all pending local notifications created by createLocalNotification ## clearNotificationCenter ```javascript PushNotification.prototype.clearNotificationCenter = function() ``` *[android]*\ Clears all notifications presented in Android Notification Center. ## setMultiNotificationMode ```javascript PushNotification.prototype.setMultiNotificationMode = function( success, fail ) ``` *[android]*\ Allows multiple notifications to be displayed in the Android Notification Center. ## setSingleNotificationMode ```javascript PushNotification.prototype.setSingleNotificationMode = function( success, fail ) ``` *[android]*\ Allows only the last notification to be displayed in Android Notification Center. ## setSoundType ```javascript PushNotification.prototype.setSoundType = function( type, success, fail ) ``` *[android]*\ Sets default sound for incoming pushes. `type` – Sound type (0 – default, 1 – no sound, 2 – always) ## setVibrateType ```javascript PushNotification.prototype.setVibrateType = function(type, success, fail ) ``` *[android]*\ Sets default vibration mode for incoming pushes. `type` – Vibration type (0 – default, 1 – no vibration, 2 – always) ## setLightScreenOnNotification ```javascript PushNotification.prototype.setLightScreenOnNotification = function( on, success, fail ) ``` *[android]*\ Turns screen on when notification arrives. `on` – enable/disable screen unlock (disabled by default) ## setEnableLED ```javascript PushNotification.prototype.setEnableLED = function( on, success, fail ) ``` *[android]*\ Enables led blinking when notification arrives and display is off. `on` – enable/disable LED blink (disabled by default) ## setColorLED ```javascript PushNotification.prototype.setColorLED = function( color, success, fail ) ``` *[android]*\ Sets led color. Use with [setEnableLED](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/cordova-plugin-api-reference/#setenableled). `color` – LED color in ARGB integer format ## getPushHistory ```javascript PushNotification.prototype.getPushHistory = function( success ) ``` *[android]*\ Returns array of received push notifications. `success` – success callback ```plaintext pushwoosh.getPushHistory(function(pushHistory) { if(pushHistory.length == 0) alert("no push history"); else alert(JSON.stringify(pushHistory)); }); pushwoosh.clearPushHistory(); ``` ## clearPushHistory ```javascript PushNotification.prototype.clearPushHistory = function() ``` *[android]*\ Clears push history. ## cancelAllLocalNotifications ```javascript PushNotification.prototype.cancelAllLocalNotifications = function( callback ) ``` *[ios]*\ Clears all local notifications from the notification center. ## presentInboxUI *[android, ios]*\ Opens the [Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox) screen. Accepts an optional style object. See [Setting up Message Inbox for Cordova](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/setting-up-message-inbox-for-cordova/) for the full list of style keys. ```javascript PushNotification.prototype.presentInboxUI = function( params ) ``` ## loadMessages *[android, ios]*\ Loads the array of inbox messages. ```javascript PushNotification.prototype.loadMessages = function( success, fail ) ``` ## unreadMessagesCount *[android, ios]*\ Returns the number of unread inbox messages. ```javascript PushNotification.prototype.unreadMessagesCount = function( success ) ``` ## messagesCount *[android, ios]*\ Returns the total number of inbox messages. ```javascript PushNotification.prototype.messagesCount = function( success ) ``` ## messagesWithNoActionPerformedCount *[android, ios]*\ Returns the number of inbox messages with no action performed. ```javascript PushNotification.prototype.messagesWithNoActionPerformedCount = function( success ) ``` ## readMessage *[android, ios]*\ Marks an inbox message as read. ```javascript PushNotification.prototype.readMessage = function( number ) ``` ## deleteMessage *[android, ios]*\ Deletes a message from the inbox. ```javascript PushNotification.prototype.deleteMessage = function( number ) ``` ## performAction *[android, ios]*\ Performs the action assigned to an inbox message, e.g. opening a URL. ```javascript PushNotification.prototype.performAction = function( number ) ``` ## setCommunicationEnabled A binary method enabling/disabling all communication with Pushwoosh. The boolean value is **false** unsubscribes the device from receiving push notifications and stops in-app messages download. The value **true** reverses the effect. ```javascript PushNotification.prototype.setCommunicationEnabled = function(enable, success, fail) ``` ## removeAllDeviceData Removes all data about the device. ```javascript PushNotification.prototype.removeAllDeviceData = function() ``` ## push-receive *[android, ios]*\ Push notification receive event. Is fired when application receives push notification in foreground or background. Closed applications does not receive this event. **Event properties** `message` – (`string`) Push notification message `userdata` – (`object`/`array`) Push notification custom data `onStart` – (`boolean`) Is launch notification `foreground` – (`boolean`) Is notification received in foreground `android` – (`object`) Android specific notification payload `ios` – (`object`) iOS specific notification payload `windows` – (`object`) Windows specific notification payload ```plaintext document.addEventListener('push-receive', function(event) { var userData = event.notification.userdata; if (typeof(userData) != "undefined") { // handle custom notification data console.warn('user data: ' + JSON.stringify(userData)); } } ); ``` ## Foreground notifications By default, the Pushwoosh plugin displays notifications in the foreground on both iOS and Android and automatically triggers the `push-receive` event. To hide foreground notifications, see the [plugin customization guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/customizing-cordova-plugin/#push-notifications-in-the-foreground). ### push-notification *[android, ios, wp8, windows]*\ Push notification accept event. It is fired when user taps the push notification. ```plaintext document.addEventListener('push-notification', function(event) { var message = event.notification.message; var userData = event.notification.userdata; if (typeof(userData) != "undefined") { console.warn('user data: ' + JSON.stringify(userData)); } } ); ``` **Event properties** Same as [push-receive](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/cordova-plugin-api-reference/#push-receive) ### additionalAuthorizationOptions *[ios only]*\ Provides _a_dditional [notification authorization options](https://developer.apple.com/documentation/usernotifications/unauthorizationoptions?language=objc). Should be called before calling **registerDevice**. ```plaintext pushwoosh.additionalAuthorizationOptions({ "UNAuthorizationOptionCriticalAlert" : 1, "UNAuthorizationOptionProvisional": 0 // set 0 or don't specify the option if you don't want to add it to your app. }); ``` --- # Customizing Cordova plugin **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/customizing-cordova-plugin/ Learn how to customize Pushwoosh SDK for your Cordova project including foreground notifications, custom sounds, icons, and deep linking --- ## Push notifications in the foreground When receiving a push in the background, no events are triggered until a push notification is clicked. After it is opened, the Pushwoosh plugin fires `push-receive` and `push-notification`events. When a push is received in the foreground, the plugin fires `push-receive` automatically. By default, it also displays a foreground notification. When the user opens it, the plugin fires `push-notification`. You can listen to `push-receive` event to immediately react on a push in case it is received in the foreground, e.g. update content on a current page in your app. `push-notification`, on the other side, is used to react to a notification click event, which requires user interaction, e.g. to navigate within your app, trigger a new process in your app, etc. By default, the plugin displays foreground notifications on both iOS and Android. To change this, set the **IOS_FOREGROUND_ALERT_TYPE** and **ANDROID_FOREGROUND_PUSH** variables when you install the plugin: **IOS_FOREGROUND_ALERT_TYPE** * `ALERT` – display foreground notifications (default) * `NONE` – hide foreground notifications Any value other than `NONE`, including `BANNER`, works the same way as `ALERT`. **ANDROID_FOREGROUND_PUSH** * `true` – display foreground notifications (default) * `false` – hide foreground notifications Example that hides foreground notifications on both platforms: ```plaintext cordova plugin add pushwoosh-cordova-plugin --variable IOS_FOREGROUND_ALERT_TYPE="NONE" --variable ANDROID_FOREGROUND_PUSH="false" ``` To switch foreground notifications at runtime, call `setShowPushnotificationAlert(show)` on the `pushwoosh` object. Pass `true` to display foreground notifications and `false` to hide them. On iOS, call it after `registerDevice`. When `ANDROID_FOREGROUND_PUSH` is `true`, `setShowPushnotificationAlert(false)` does not hide foreground notifications on Android. To control them at runtime on Android, install the plugin with `ANDROID_FOREGROUND_PUSH="false"`. The plugin writes `ANDROID_FOREGROUND_PUSH` to the `com.pushwoosh.foreground_push` key in `AndroidManifest.xml`. In a Cordova app, `true` in this key shows foreground notifications. In the native Android SDK, the same key has the opposite meaning: `true` hides foreground notifications. ## Custom push sound To enable custom notification sound on Android, place the sound files in **www/res** folder and specify every sound in `config.xml`: ```txt ... ... <-- Add this line for each sound --> ``` The sounds will be accessible in the **Sound** dropdown menu of the **Send Push** panel. On iOS, sound files are not automatically associated with the Pushwoosh Application yet, but can be used manually, e.g. `www/res/sound_name.wav`. Caution #### iOS iOS notifications support only .aiff, .wav and, .caf file formats. ## Custom push icon Push notification icon can be customized on Android locally by using `pw_notification` as resource name for such icon (see sample [1](https://github.com/Pushwoosh/pushwoosh-phonegap-cordova-sample/blob/master/Cordova/hooks/after_platform_add/2_set_notification_icon.js) [2](https://github.com/Pushwoosh/pushwoosh-phonegap-cordova-sample/tree/master/Cordova/config/android/res)); or remotely by referencing any resource name in the **Icon** setting in the **Send Push** panel. ## Custom push data Custom push data can be sent using the **Action** setting in the **Send Push** panel. To receive and handle such data use the following code: ```javascript document.addEventListener('push-notification', function(event) { var message = event.notification.message; // Push message var userData = event.notification.userdata; // Custom push data if (userData) { // handle custom push data here } } ); ``` ## Controlling Log level In order to assist with debugging and integration, SDK will print all the requests to the console by default. When you are ready for the production build, set `LOG_LEVEL` variable to one of the following values: * `NONE` – no logs from the SDK * `ERROR` – only display errors in the console * `WARNING` – show warnings * `INFO` – show informational messages * `DEBUG` – show everything, including debug information (default) Example: ```plaintext cordova plugin add pushwoosh-cordova-plugin --variable LOG_LEVEL="INFO" ``` ## Deep linking We recommend using \ It’s super easy to set deep links for Cordova and it works with Pushwoosh deep linking functionality out of the box. You can find installation and usage in [this guide](https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking/) or in the [plugin docs](https://github.com/EddyVerbruggen/Custom-URL-scheme#2-installation). ## Rich Media JS Bridge You can call your JS functions from Rich Media in Cordova WebView via javascript interfaces.\ A call from a Rich Media must have the following format: .callFunction(‘’, ) Example: **1.** Create Rich Media with a javascript call: ```javascript testBridge.callFunction('testFunction', JSON.stringify({'param1' : 1, 'param2':'test'})) ``` **2.** Add a javascript interface to your project: ```javascript pushNotification.addJavaScriptInterface('testBridge'); ``` **3.** Add function: ```javascript function testFunction(params) { alert("Bridge is working! " + params.param1 + " " + params.param2); } ``` ## Using Pushwoosh plugin with other FCM services If your app uses other plugins that rely on **firebase-messaging**, it might result in conflicts, or in one of the plugins not working as expected. To resolve this conflict, you can add a custom FirebaseMessagingService class that will route push notifications between the plugins. Pushwoosh Cordova plugin contains a template of this class which you can use as is to force all notifications to go through Pushwoosh handlers or modify it to handle notifications from other providers as well. To enable the template, uncomment the following lines in the plugin’s `plugin.xml`: ```plaintext ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Cordova SDK Basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/basic-integration-guide/ Learn how to integrate the Pushwoosh Cordova SDK, configure users, track events, set tags and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh Cordova SDK into your application. ## Prerequisites To integrate the Pushwoosh Cordova SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Add Pushwoosh Cordova SDK Dependency Add the Pushwoosh Cordova SDK dependency to your project: ```bash cordova plugin add pushwoosh-cordova-plugin ``` ### 2. Cordova SDK Initialization In the root component of your `index.js` file, add the following code inside the `deviceready` event handler. Follow the steps in exact order: index.js ```javascript document.addEventListener('deviceready', function() { var pushwoosh = cordova.require("pushwoosh-cordova-plugin.PushNotification"); // 1. Register notification callbacks before initialization document.addEventListener('push-receive', function(event) { var notification = event.notification; console.log("Push received: " + JSON.stringify(notification)); }); document.addEventListener('push-notification', function(event) { var notification = event.notification; console.log("Push opened: " + JSON.stringify(notification)); }); // 2. Initialize Pushwoosh pushwoosh.onDeviceReady({ appid: "__YOUR_APP_ID__" }); // 3. Register the device to receive push notifications pushwoosh.registerDevice( function(status) { var pushToken = status.pushToken; // Handle successful registration }, function(status) { // Handle registration error } ); }, false); ``` Where: * `__YOUR_APP_ID__` is the application code from the Pushwoosh Control Panel. Initialization order matters The initialization sequence **must** follow the exact order shown above: 1. **Register event listeners first** (`push-receive`, `push-notification`) 2. **Then** call `onDeviceReady()` 3. **Then** call `registerDevice()` Changing this order can cause the following issues: * **Event listeners registered after `onDeviceReady()`:** If the app was launched by tapping a push notification (cold start), `onDeviceReady()` immediately delivers the launch notification payload to JavaScript. If your listeners are not yet registered at that point, **the launch notification is lost** with no way to recover it. * **`registerDevice()` called before `onDeviceReady()`:** The native SDK may not be properly configured with your App ID yet, which can cause the device registration to fail silently or return an error. * **Event listeners registered after `registerDevice()`:** Any push notification that arrives and is processed before your listeners are in place will be dispatched as a DOM event and **silently dropped** since there is no replay mechanism in the plugin. The plugin does not queue or buffer missed events on the JavaScript side. DOM events fired by `document.dispatchEvent()` are delivered only to listeners that are already registered at the time of dispatch. ### 3. iOS Native Setup #### 3.1 Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. #### 3.2 Info.plist In your `Runner/Info.plist` set the `__PUSHWOOSH_DEVICE_API_TOKEN__` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): info.plist ```swift Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` #### 3.3 Message delivery tracking You must add a Notification Service Extension target to your project. This is essential for accurate delivery tracking and features like Rich Media on iOS. Follow the [native guide’s steps](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target and the necessary Pushwoosh code within it. ### 4. Android Native Setup #### 4.1 Install dependencies Ensure that the required dependencies and plugins are added to your Gradle scripts: Add the Google Services Gradle plugin to your project-level `build.gradle` dependencies: android/build.gradle ```groovy buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' } } ``` Apply the plugin in your app-level `build.gradle` file: app/build.gradle ```groovy apply plugin: 'com.google.gms.google-services' ``` #### 4.2 Add Firebase configuration file Place the `google-services.json` file into `android/app` folder in your project directory. #### 4.3 Add Pushwoosh metadata In your `main/AndroidManifest.xml` add [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) inside the `` tag: AndroidManifest.xml ```xml ``` > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 5. Run the Project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notification event listeners In the Pushwoosh SDK there are two event listeners, designed for handling push notifications: * `push-receive` event is triggered when a push notification is received while the app is in the foreground * `push-notification` event is triggered when a user opens a notification These event listeners **must** be registered **before** calling `onDeviceReady()`, as shown in the [initialization step above](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/basic-integration-guide/#2-cordova-sdk-initialization). You can customize the handler logic to suit your needs: index.js ```javascript // Register before onDeviceReady() document.addEventListener('push-receive', function(event) { var message = event.notification.message; var payload = event.notification.userdata; console.log("Push received: " + message); // Add your custom logic here }); document.addEventListener('push-notification', function(event) { var message = event.notification.message; var payload = event.notification.userdata; console.log("Push accepted: " + message); // Add your custom logic here (e.g., navigate to a specific screen) }); ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty ```javascript class Registration { afterUserLogin(user) { // Set user ID pushwoosh.setUserId(user.getId()); // Setting additional user information as tags for Pushwoosh pushwoosh.setTags({ "age": user.getAge(), "name": user.getName(), "last_login": user.getLastLoginDate() }); } } ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. ```javascript class UpdateUser { afterUserUpdateProfile(user) { // Set list of favorite categories pushwoosh.setTags({ "favorite_categories": user.getFavoriteCategoriesList() }); // Set payment information pushwoosh.setTags({ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() }); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions ```javascript class Registration { // Track login event afterUserLogin(user) { pushwoosh.postEvent("login", { "name": user.getName(), "last_login": user.getLastLoginDate() }); } // Track purchase event afterUserPurchase(product) { pushwoosh.postEvent("purchase", { "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() }); } } ``` ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # Integrating Cordova Huawei plugin **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/integrating-cordova-huawei-plugin/ Learn how to set up integration with Cordova Huawei plugin for Pushwoosh push notifications on Huawei devices --- ## Integrate Cordova plugin Integrate Cordova plugin into your project following the steps described in the [Integrating Cordova Plugin](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integrating-cordova-plugin/). ## Configure Huawei via AppGallery Connect 1. Go to [AppGallery Connect -> Projects List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject) and select a project to configure. 2. Press **agconnect-services.json** in the App information section to download a configuration file. 3. Put the downloaded file into your Android Studio project (`YOUR_PROJECT/app/agconnect-services.json`). ## Generate Signing certificate fingerprint 1. In Android Studio, go to View > Tool Windows > Gradle. 2. In the window opened, go to app > Tasks > android > signingReport. 3. Copy SHA-256 from your release keystore. You can also add the SHA-256 from your debug keystore for testing purposes. 4. Select your project in [AppGallery Connect Project List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject). 5. Paste the SHA-256 into the **SHA-256 certificate fingerprint** field. Please be aware that fingerprint processing may take up to 10 minutes. ## Configure Huawei in Pushwoosh Follow the guide to configure the Huawei platform for your Pushwoosh project: [Configure Huawei](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration/). Add the **agconnect-services.json** to **android/app**. ## Add Huawei repo Add the [maven repository](https://developer.huawei.com/repo/) to your project’s **android/repositories.gradle** and **android/app/repositories.gradle** files. ## Add dependencies 1. Add **classpath “com.huawei.agconnect:agcp:1.2.1.301”** to the **android/build.gradle** file of **buildScript/dependencies**. 2. Add **apply plugin: ‘com.huawei.agconnect’** and **implementation “com.huawei.hms:push:6.1.0.300** to **android/app/build.gradle.** ## Enable push notifications In your JavaScript code, add **enableHuaweiPushNotifications** before executing **onDeviceReady:** ```javascript var pushNotification = cordova.require("pushwoosh-cordova-plugin.PushNotification"); pushNotification.enableHuaweiPushNotifications(); pushNotification.onDeviceReady({ projectid: "GOOGLE_PROJECT_ID", appid: "PUSHWOOSH_APP_ID", serviceName: "" }); ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Cordova SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/quick-start/ Get started with the Pushwoosh Cordova SDK quickly by cloning the example project and configuring it with your credentials --- Get started with the Pushwoosh Cordova SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh Cordova SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project ```bash # Clone the SDK repository and navigate to the example project git clone https://github.com/Pushwoosh/pushwoosh-phonegap-plugin.git # Navigate to example directory cd pushwoosh-phonegap-plugin/example/newdemo/ ``` ## Configure the project Install the dependencies: ```bash npm i ``` Add the Pushwoosh plugin: ```bash cordova plugin add ../../ --link --nofetch ``` Open **www/js/index.js** and update the `pushwoosh.onDeviceReady` call: ```javascript pushwoosh.onDeviceReady({ appid: "XXXXX-XXXXX" }); ``` Replace the `appid` value with your **Pushwoosh App Code**. ### iOS ###### 1. Set the Bundle Identifier Open the **platforms/ios/newdemo.xcworkspace** file in Xcode. Set the **Bundle Identifier** for each target: * `newdemo`: your app’s package name. (eg. com.example.pushwoosh) ###### 2. Add capabilities To enable Push Notifications, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. ###### 3. Set the API Token In your `ios/newdemo/Info.plist` set the `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### Android ###### 1. Setup google-services.json Copy your `google-services.json` file to the root directory of the project ###### 2. Set the package name Open **android/app/build.gradle** and set the **applicationId** to your app’s package name. (eg. `com.example.pushwoosh`) ###### 3. Set the API Token Open your **android/app/src/main/AndroidManifest.xml** file and add the following metadata inside the `` tag: AndroidManifest.xml ```xml ``` Replace `__YOUR_PUSHWOOSH_DEVICE_API_TOKEN__` with your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). ## Run the project 1. Build and run the project. 2. Toggle the **Register for notifications** switch. 3. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Setting up badges for Cordova **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/setting-up-badges-for-cordova/ This guide describes the process of implementing Badges for Pushwoosh Cordova plugin --- Since the Cordova plugin does not contain the PWNotificationExtensionManager header, you have to perform several additional steps to implement Badges for your Cordova project. To set up Badges, complete the following steps: 1. Create Notification Service Extension 2. Add Pushwoosh framework 3. Add the App Groups capability Note To implement badges for iOS Native app, please refer to the [Setting up Badges](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges/) guide. ### 1. Add Notification Service Extension (File -> New -> Target…) ### 2. Select the “Notification Service Extension” ### 3. Add Pushwoosh.xcframework to NotificationService extension target as follows: 1. Choose **NotificationService** extension target. 2. Go to the “**General**” tab. 3. Find ‘**Framework and Libraries**’ and press ’**+**’ button. 4. Select **Pushwoosh.xcframework.** ### 4. Update the Notification Service Extension code as follows: ```plaintext #import "NotificationService.h" #import "PWNotificationExtensionManager.h" @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end ``` ### 5. Add the “App Groups Capability” for each target of your application ![App Groups Capability](https://docs.pushwoosh.com/cordova-setting-up-badges-for-cordova-3.webp) ![Info.plist](https://docs.pushwoosh.com/cordova-setting-up-badges-for-cordova-4.webp) ### 6. Add the App Groups ID to your info.plist for each target of your application ```plaintext PW_APP_GROUPS_NAME group.com.example.demoapp_example ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Setting up Message Inbox for Cordova **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/setting-up-message-inbox-for-cordova/ Show the Message Inbox screen in a Cordova app, style it, and manage messages with the Pushwoosh Cordova plugin's Inbox API. --- The Pushwoosh Cordova plugin ships Message Inbox (the “bell icon” App Inbox) as part of the same `pushwoosh-cordova-plugin` dependency used for push notifications. No extra install step is needed. ## Prerequisites * The [base Pushwoosh Cordova SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/basic-integration-guide/) already integrated and sending pushes. ## Show the inbox Call `presentInboxUI()` on the same `pushwoosh` object you initialized with `cordova.require`: ```javascript var pushwoosh = cordova.require("pushwoosh-cordova-plugin.PushNotification"); pushwoosh.presentInboxUI(); ``` This opens a native screen (a view controller on iOS, an Activity on Android) with the SDK’s default appearance. ## Customize the style Pass a style object to `presentInboxUI()`. Every key is optional: | Key | Customizes | | ---------------------- | -------------------------------------------------- | | `dateFormat` | Date formatting | | `defaultImageIcon` | The default icon next to a message with no image | | `unreadImage` | The unread-messages mark (iOS only) | | `listErrorImage` | Shown when loading the list fails | | `listEmptyImage` | Shown when the list has no messages | | `listErrorMessage` | Error text (not localized) | | `listEmptyMessage` | Empty-list text (not localized) | | `defaultTextColor` | Default text color (iOS only) | | `accentColor` | Accent color | | `backgroundColor` | Background color | | `highlightColor` | Selection color | | `titleColor` | Message title color | | `readTitleColor` | Title color for read messages (Android only) | | `descriptionColor` | Message description color | | `readDescriptionColor` | Description color for read messages (Android only) | | `dateColor` | Message date color | | `readDateColor` | Date color for read messages (Android only) | | `dividerColor` | Separator color | | `barTitle` | The inbox screen’s title | | `barBackgroundColor` | The title bar’s background color | | `barAccentColor` | The title bar’s back-button color | | `barTextColor` | The title bar’s text color | ```javascript pushwoosh.presentInboxUI({ dateFormat: "dd.MM.yyyy", accentColor: "#3498db", backgroundColor: "#ffffff", titleColor: "#333333", descriptionColor: "#666666", listEmptyMessage: "No messages yet" }); ``` ## Manage messages programmatically Load the message list, read counts, and act on individual messages without opening the built-in screen. This is useful for a custom inbox UI or an unread badge: ```javascript // Array of InboxNotification objects pushwoosh.loadMessages(function(messages) { messages.forEach(function(message) { console.log(message.title + ": " + message.message); }); }, function(error) { console.error("Failed to load: " + error); }); pushwoosh.unreadMessagesCount(function(count) { console.log("Unread messages: " + count); }); pushwoosh.messagesCount(function(count) { /* total messages */ }); pushwoosh.messagesWithNoActionPerformedCount(function(count) { /* not yet acted on */ }); pushwoosh.readMessage(messageCode); pushwoosh.deleteMessage(messageCode); pushwoosh.performAction(messageCode); // runs the message's default action, e.g. opening a URL ``` --- # Cordova VoIP Calls **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/voip-cordova/ Set up VoIP calls with the Pushwoosh Cordova plugin, including iOS background modes, a custom ringtone, and call events. --- Pushwoosh supports VoIP-style call notifications for iOS and Android using the `pushwoosh-cordova-plugin`. This enables native VoIP call functionality, including incoming call UI and call control events. Before you start, integrate the plugin with your Pushwoosh Application Code as described in the [basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/integration/basic-integration-guide/). Note VoIP pushes use the same Application Code as regular pushes, so no separate VoIP application is needed. The `setVoipAppCode()` method was removed in plugin version 8.3.57. If your app calls it, remove the call when you upgrade. ## 1. Add the Pushwoosh Cordova plugin with VoIP enabled Install the plugin with the VoIP flag enabled: ```sh cordova plugin add pushwoosh-cordova-plugin --variable PW_VOIP_IOS_ENABLED=true --variable PW_VOIP_ANDROID_ENABLED=true ``` This will configure the plugin to support VoIP functionality on iOS. ## 2. Check the PushwooshVoIP dependency The plugin links the `PushwooshVoIP` module automatically. On cordova-ios 8, it adds the `PushwooshVoIP` Swift package and doesn’t change the `Podfile`. On earlier cordova-ios versions, it adds this line to your `Podfile`: ```ruby pod 'PushwooshXCFramework/PushwooshVoIP' ``` > This module is required to support PushKit-based VoIP notifications and system-level call handling. ## 3. Enable VoIP background mode in Xcode Open your project in Xcode and follow these steps: 1. Go to **Signing & Capabilities**. 2. Add **Background Modes** capability. 3. Check the **Voice over IP** option. This allows your app to receive incoming VoIP pushes while in the background. ## 4. Request call permission on Android On Android, the app needs the user’s permission to receive calls. Request it with `requestCallPermission()`. If the user denies it, the app ignores incoming VoIP calls. ```javascript pushwoosh.requestCallPermission(); ``` ## 5. Set a custom ringtone (optional) The plugin initializes VoIP automatically. To play a custom ringtone for incoming calls, pass a sound file name from the app bundle: ```javascript pushwoosh.setRingtone("incoming.caf"); ``` Note `initializeVoIPParameters()` is deprecated. It only sets the ringtone, and its `supportsVideo` and `handleTypes` parameters are ignored. ## 6. Available VoIP callbacks and methods Video support and the caller ID format are set per call in the VoIP push payload: `video` and, on iOS, `handleType` (`1` for generic, `2` for phone number, `3` for email). The Cordova plugin supports the following VoIP-related methods and events: * `answer` – called when a user answers an incoming call * `endCall` – called when a user ends the current call * `hangup` – called when a call is hung up * `reject` – called when an incoming call is rejected * `muted` – toggle mute status, iOS-only * `held` – toggle hold status, iOS-only * `voipPushPayload` – returns push payload for the incoming VoIP call * `incomingCallSuccess` – called when system call UI is successfully shown, iOS-only * `incomingCallFailure` – called when system call UI fails to show, iOS-only * `speakerOn` – turn speaker on * `speakerOff` – turn speaker off * `playDTMF` – play DTMF tone during a call, iOS-only * `voipDidRegisterTokenSuccessfully` – called when the VoIP token is registered with Pushwoosh, iOS-only * `voipDidFailToRegisterTokenWithError` – called when VoIP token registration fails, iOS-only * `voipDidCancelCall` – called when a call is cancelled remotely via a VoIP push * `voipDidFailToCancelCall` – called when a call cancellation attempt fails These can be hooked using Cordova’s `PushNotification` object, allowing you to control and respond to VoIP call state changes. On Android, you can also handle these events in your own native Java class, for example to open your own call screen when the user answers. See [Custom call event handler on Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/android-custom-call-event-handler/). ## Related links * [Pushwoosh Cordova SDK Overview](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/) * [VoIP Push Notifications on iOS (Apple Docs)](https://developer.apple.com/documentation/pushkit) --- # Expo SDK Basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/expo/integrating-expo-plugin/ Learn how to integrate the Pushwoosh Expo SDK, configure users, track events, set tags, and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh Expo SDK into your application. ## Prerequisites To integrate the Pushwoosh Expo SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Install the plugin Install the Pushwoosh Expo plugin using the Expo CLI ```bash expo install pushwoosh-expo-plugin ``` Install Pushwoosh React Native SDK ```bash npm install pushwoosh-react-native-plugin --save ``` ### 2. Set the plugin properties Add the plugin to the front of the plugin array with the necessary properties: app.json/app.config.js ```json { "expo": { "plugins": [ [ "pushwoosh-expo-plugin", { "mode": "development", "ios": { "PW_API_TOKEN": "__YOUR_DEVICE_API_TOKEN__" }, "android": { "apiToken": "__YOUR_DEVICE_API_TOKEN__" } } ] ] } } ``` Where: * `mode` is used to configure the APNs environment entitlement. “Development” or “production” values are available. * `PW_API_TOKEN`, `apiToken` is your [Pushwoosh Device API Token.](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### 3. Initialize Pushwoosh In the root component of your application: * Import the `pushwoosh-react-native-plugin` plugin. * Initialize the Pushwoosh SDK. * Call `register()` in your initialization logic to register for push notifications. index.tsx ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; Pushwoosh.init({ "pw_appid": "__YOUR_APP_ID__" }); Pushwoosh.register(); ``` Where: * `__YOUR_APP_ID__` is the application code from the Pushwoosh Control Panel. ### 4. Android Native Setup Add Firebase configuration file: 1. Copy your `google-services.json` file to the root directory of the project. 2. Set the `googleServicesFile` property to the path of your `google-services.json` and specify the `package` property: app.json/app.config.js ```json "expo": { "name": "sample", "android": { "package": "com.pushwoosh.sample", "googleServicesFile": "./google-services.json" }, "plugins": [ [ "pushwoosh-expo-plugin", { "mode": "development", "ios": { "PW_API_TOKEN": "__YOUR_DEVICE_API_TOKEN__" }, "android": { "apiToken": "__YOUR_DEVICE_API_TOKEN__" } } ] ] } ``` ### 5. iOS Native Setup Set `bundleIdentifier` property to the `ios` object: app.json/app.config.js ```json "expo": { "name": "sample", "ios": { "bundleIdentifier": "com.pushwoosh.sample" }, "plugins": [ [ "pushwoosh-expo-plugin", { "mode": "development", "ios": { "PW_API_TOKEN": "__YOUR_DEVICE_API_TOKEN__" }, "android": { "apiToken": "__YOUR_DEVICE_API_TOKEN__" } } ] ] } ``` ### 6. Prebuild the app Generate native code and configure the dependencies for each platform by running prebuild: ```bash npx expo prebuild ``` ### 7. Run the project 1. Build and run the project: * Android ```bash npx expo run:android ``` * iOS ```bash npx expo run:ios ``` 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notification event listeners In the Pushwoosh SDK there are two event listeners, designed for handling push notifications: * `onPushReceived` event is triggered, when a push notification is received * `onPushAccepted` event is triggered, when a user opens a notification You should set up these event listeners right after initialization of the SDK at the application start up: ```typescript import { DeviceEventEmitter } from 'react-native'; import Pushwoosh from 'pushwoosh-react-native-plugin'; class PushwooshNotificationHandler { setupPushListeners(): void { DeviceEventEmitter.addListener("pushReceived", (e) => { console.warn("Push received: " + JSON.stringify(e)); }); DeviceEventEmitter.addListener("pushOpened", (e) => { console.warn("Push opened:" + JSON.stringify(e)); }); } } ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty ```typescript import Pushwoosh from 'pushwoosh-react-native-plugin'; class Registration { afterUserLogin(user: User): void { // Set user ID Pushwoosh.setUserId(user.getId()); // Set user email Pushwoosh.setEmails(user.getEmailList()); // Setting additional user information as tags for Pushwoosh Pushwoosh.setTags({ "age": user.getAge(), "name": user.getName(), "last_login": user.getLastLoginDate() }); } } ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. ```typescript import Pushwoosh from 'pushwoosh-react-native-plugin'; class UpdateUser { afterUserUpdateProfile(user: User): void { // Set list of favorite categories Pushwoosh.setTags({ "favorite_categories": user.getFavoriteCategoriesList() }); // Set payment information Pushwoosh.setTags({ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() }); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions ```typescript import Pushwoosh from 'pushwoosh-react-native-plugin'; class Registration { // Track login event afterUserLogin(user: User): void { Pushwoosh.postEvent("login", { "name": user.getName(), "last_login": user.getLastLoginDate() }); } // Track purchase event afterUserPurchase(product: Product): void { Pushwoosh.postEvent("purchase", { "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() }); } } ``` ### Message delivery tracking for iOS You must add a Notification Service Extension target to your project. This is essential for accurate delivery tracking and features like Rich Media on iOS. Follow the [native guide’s steps](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target and the necessary Pushwoosh code within it. ### Additional plugin properties | Property | Default | Description | | ----------------------- | ------- | ------------------------------------------------------------------------------------------- | | **iOS properties** | | | | `Pushwoosh_LOG_LEVEL` | `INFO` | Log level for iOS. Possible values: `NONE`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `NOISE` | | **Android properties** | | | | `logLevel` | `INFO` | Log level for Android. One of: `NONE`, `ERROR`, `WARN`, `INFO`, `DEBUG`, `NOISE` | | `multiNotificationMode` | `true` | Can be changed to false in case you want to display only the last notification for the user | | `icon` | - | Path to a custom notification icon for Android | ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. ###### FCM registration error: Failed to retrieve token. Is firebase configured correctly? Make sure your Firebase `googleServicesFile` property is set up in the Expo configuration file and the `google-services.json` file is added to the root directory of your project: app.json/app.config.js ```json "expo": { "name": "sample", "android": { "package": "com.pushwoosh.sample", "googleServicesFile": "./google-services.json" }, "plugins": [ [ "pushwoosh-expo-plugin", { "mode": "development", "ios": { "PW_API_TOKEN": "__YOUR_DEVICE_API_TOKEN__" }, "android": { "apiToken": "__YOUR_DEVICE_API_TOKEN__" } } ] ] } ``` ###### TypeError: Cannot read property ‘init’ of null You might encounter the error, when attempting to run the app on a device.\ To resolve the issue, make sure you’ve completed the prebuild step. It generates the native code and configures the dependencies for each platform. ```bash npx expo prebuild ``` --- # Expo SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/expo/quick-start/ Get started with the Pushwoosh Expo SDK using this quick and simple example. --- Get started with the Pushwoosh Expo SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh Expo SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project ```bash # Clone the SDK repository and navigate to the example project git clone https://github.com/Pushwoosh/pushwoosh-expo-plugin-sample.git # Navigate to example directory cd pushwoosh-expo-plugin-sample/ ``` ## Configure the project ##### 1. Install the dependencies: ```bash npm install ``` ##### 2. Initialize Pushwoosh: Open **app/_layout.tsx** and update the Pushwoosh.init call: ```typescript Pushwoosh.init({ pw_appid: "__YOUR_APP_CODE__" }); ``` Where: `__YOUR_APP_CODE__` is the application code from the Pushwoosh Control Panel. ##### 3. Set the package name and bundle ID: Open **app.json** and set: * `package` to your app’s package name. * `bundleIdentifier` to your app’s bundle ID. ##### 4. Set the Pushwoosh Device API token: Open your **app.json** file and set [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) for both platforms: * `PW_API_TOKEN` device API token value for iOS platform. * `apiToken` device API token value for Android platform. ##### 5. Add Firebase configuration file: Copy your `google-services.json` file to the root directory of the project. ## Prebuild the app Generate native code and configure the dependencies for each platform by running prebuild: ```bash npx expo prebuild ``` ## Build and run the project 1. Build the project: * Android ```bash npx expo run:android ``` * iOS ```bash npx expo run:ios ``` 2. Grant permission for push notifications. The device will be registered with Pushwoosh and push notifications are enabled. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Flutter SDK Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/ Explore the official Pushwoosh Flutter SDK documentation for integration guides, API reference, configuration options, and examples. --- ## Setting up Flutter SDK Choose the integration method that best suits your needs to get started with the Pushwoosh Flutter SDK: * [AI-Assisted Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/ai-integration/) - use AI coding assistants to integrate and configure the Pushwoosh Flutter plugin Or doing by yourself: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/integration/quick-start/) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/integration/basic-integration-guide/) - a step-by-step approach to setting up the Flutter SDK from scratch --- # AI-Assisted Integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/ai-integration/ Integrate Pushwoosh Flutter plugin using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP --- Use AI coding assistants to integrate and configure the Pushwoosh Flutter plugin in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date plugin documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh Flutter plugin docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh Flutter plugin library ID on Context7: **`/pushwoosh/pushwoosh-flutter`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the plugin. Below are ready-to-use prompts for common tasks. ### Basic plugin integration ```text Integrate Pushwoosh Flutter plugin into my Flutter project. Requirements: - Install pushwoosh_flutter via pub - Initialize Pushwoosh with my App ID in main() - Register for push notifications and handle onPushReceived and onPushAccepted streams Use Context7 MCP to fetch Pushwoosh Flutter plugin documentation. ``` ### Tags and user segmentation ```text Show me how to use Pushwoosh tags in a Flutter app for user segmentation. I need to set tags, get tags, and set user ID for cross-device tracking. Use Context7 MCP to fetch Pushwoosh Flutter plugin documentation for setTags and getTags. ``` ### Message Inbox integration ```text Integrate Pushwoosh Message Inbox into my Flutter app. Show me how to: - Display the inbox UI with custom styling using PWInboxStyle - Load messages programmatically - Track unread message count Use Context7 MCP to fetch Pushwoosh Flutter plugin documentation for presentInboxUI. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh Flutter plugin documentation”** at the end of your prompt tells the AI assistant to pull the latest plugin docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh Flutter plugin, it receives: * **Installation instructions** — pubspec.yaml setup and platform-specific configuration * **API reference** — public classes, methods, and stream definitions * **Code examples** — Dart snippets for common use cases * **Plugin documentation** — guides for core plugin, Inbox, and Geozones * **Configuration details** — initialization parameters, iOS and Android setup steps --- # Live Activity iOS in a Flutter project **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/flutter-ios-live-activities/ Configuring Live Activity iOS in a Flutter project --- We will create a Live Activity with a Stopwatch implementation for a Flutter project. The project will include a Flutter-side implementation as well as native implementation in the Xcode project. ## Flutter side implementation We will create a class that will implement three methods: * `startLiveActivity()` * `updateLiveActivity()` * `stopLiveActivity()`. ```dart class DynamicIslandManager { final String channelKey; late final MethodChannel _methodChannel; DynamicIslandManager({required this.channelKey}) { _methodChannel = MethodChannel(channelKey); } Future startLiveActivity({required Map jsonData}) async { try { await _methodChannel.invokeListMethod('startLiveActivity', jsonData); } catch (e, st) { log(e.toString(), stackTrace: st); } } Future updateLiveActivity( {required Map jsonData}) async { try { await _methodChannel.invokeListMethod('updateLiveActivity', jsonData); } catch (e, st) { log(e.toString(), stackTrace: st); } } Future stopLiveActivity() async { try { await _methodChannel.invokeListMethod('stopLiveActivity'); } catch (e, st) { log(e.toString(), stackTrace: st); } } } ``` We will be building a stopwatch app, and to pass the stopwatch timer data to native methods, we need to create a data model and send it to the method channel invocation in a JSON-like map format. ```dart class DynamicIslandStopwatchDataModel { final int elapsedSeconds; DynamicIslandStopwatchDataModel({ required this.elapsedSeconds, }); Map toMap() { return { 'elapsedSeconds': elapsedSeconds, }; } } ``` Each method will include the necessary code to call the corresponding native methods. This was the initial setup required to run it on the Flutter side. Following this, a basic stopwatch app UI with the necessary method calls was implemented. ```dart class _StopWatchScreenState extends State { int seconds = 0; bool isRunning = false; Timer? timer; /// channel key is used to send data from flutter to swift side over /// a unique bridge (link between flutter & swift) final DynamicIslandManager diManager = DynamicIslandManager(channelKey: 'PW'); void startTimer() { setState(() { isRunning = true; }); // invoking startLiveActivity Method diManager.startLiveActivity( jsonData: DynamicIslandStopwatchDataModel(elapsedSeconds: 0).toMap(), ); timer = Timer.periodic(const Duration(seconds: 1), (timer) { setState(() { seconds++; }); // invoking the updateLiveActivity Method diManager.updateLiveActivity( jsonData: DynamicIslandStopwatchDataModel( elapsedSeconds: seconds, ).toMap(), ); }); } void stopTimer() { timer?.cancel(); setState(() { seconds = 0; isRunning = false; }); // invoking the stopLiveActivity Method diManager.stopLiveActivity(); } @override void dispose() { timer?.cancel(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Stopwatch App'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Stopwatch: $seconds seconds', style: const TextStyle(fontSize: 24), ), const SizedBox(height: 20), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ ElevatedButton( onPressed: isRunning ? null : startTimer, child: const Text('Start'), ), const SizedBox(width: 20), ElevatedButton( onPressed: isRunning ? stopTimer : null, child: const Text('Stop'), ), ], ), ], ), ), ); } } ``` ## Native side implementation ### Xcode project configuration 1. Open your project on Xcode and in General Tab set the minimum deployment to iOS 16.1 ![Xcode General Tab showing minimum deployment set to iOS 16.1](https://docs.pushwoosh.com/flutter-flutter-ios-live-activities-1.webp) 2. In `Info.plist` add a new key `NSSupportsLiveActivities` and set its value to `YES` (Boolean). ### Info.plist ```xml NSSupportsLiveActivities ``` 3. From action bar, select **File > New > Target** and search for `WidgetExtensionand` create a new widget. ## Code side implementation Let’s create a class called LiveActivityManager that will manage the live activities for our Dynamic Island. This class will contain three methods: `startLiveActivity()`, `updateLiveActivity()`, and `stopLiveActivity()`. **Swift** ```swift import ActivityKit import Flutter import Foundation @available(iOS 16.1, *) class LiveActivityManager { private var stopwatchActivity: Activity? = nil func startLiveActivity(data: [String: Any]?, result: FlutterResult) { let attributes = StopwatchWidgetAttributes() if let info = data { let state = StopwatchWidgetAttributes.ContentState( elapsedTime: info["elapsedSeconds"] as? Int ?? 0 ) stopwatchActivity = try? Activity.request( attributes: attributes, contentState: state, pushType: nil) } else { result(FlutterError(code: "418", message: "Live activity didn't invoked", details: nil)) } } func updateLiveActivity(data: [String: Any]?, result: FlutterResult) { if let info = data { let updatedState = StopwatchWidgetAttributes.ContentState( elapsedTime: info["elapsedSeconds"] as? Int ?? 0 ) Task { await stopwatchActivity?.update(using: updatedState) } } else { result(FlutterError(code: "418", message: "Live activity didn't updated", details: nil)) } } func stopLiveActivity(result: FlutterResult) { do { Task { await stopwatchActivity?.end(using: nil, dismissalPolicy: .immediate) } } catch { result(FlutterError(code: "418", message: error.localizedDescription, details: nil)) } } } ``` Each method has its specific functionality. `startLiveActivity()` (as the name suggests) is responsible for initiating the Live Activity, which triggers the Dynamic Island functionality. Similarly, `stopLiveActivity()` and `updateLiveActivity()` handle stopping the Live Activity and updating the data displayed on the Dynamic Island. Next, open `StopWatchDIWidgetLiveActivity.swift` and modify the `StopwatchDIWidgetAttributes` struct (as shown in the snippet). This attribute struct serves as the data model that holds the information to be displayed on the UI (directly from Flutter) and can also modify the UI as needed. **Swift** ```swift struct StopwatchWidgetAttributes: ActivityAttributes { public typealias stopwatchStatus = ContentState public struct ContentState: Codable, Hashable { var elapsedTime: Int } } ``` Now, the only thing left is the UI for the Dynamic Island! (Woohoo, we’ve come this far!) The entire UI for the Dynamic Island needs to be created using SwiftUI. For this article, a simple UI has been designed (but feel free to customize it as needed). This UI code should be written inside the `StopwatchWidgetLiveActivity` struct. Remove the existing code from the struct, and you can follow the code below: **SwiftUI** ```swift struct StopwatchWidgetLiveActivity: Widget { func getTimeString(_ seconds: Int) -> String { let hours = seconds / 3600 let minutes = (seconds % 3600) / 60 let seconds = (seconds % 3600) % 60 return hours == 0 ? String(format: "%02d:%02d", minutes, seconds) : String(format: "%02d:%02d:%02d", hours, minutes, seconds) } var body: some WidgetConfiguration { ActivityConfiguration(for: StopwatchWidgetAttributes.self) { context in HStack { Text("Time ellapsed") .font(.system(size: 20, weight: .semibold)) .foregroundColor(.white) Spacer() Image(systemName: "timer") .foregroundColor(.white) Spacer().frame(width: 10) Text(getTimeString(context.state.elapsedTime)) .font(.system(size: 24, weight: .semibold)) .foregroundColor(.yellow) } .padding(.horizontal) .activityBackgroundTint(Color.black.opacity(0.5)) } dynamicIsland: { context in DynamicIsland { DynamicIslandExpandedRegion(.center) { VStack(alignment: .center) { Text("Pushwoosh Timer") Spacer().frame(height: 24) HStack { Text("Time ellapsed") .font(.system(size: 20, weight: .semibold)) .foregroundColor(.white) Spacer() Image(systemName: "timer") Spacer().frame(width: 10) Text(getTimeString(context.state.elapsedTime)) .font(.system(size: 24, weight: .semibold)) .foregroundColor(.yellow) }.padding(.horizontal) } } } compactLeading: { Image(systemName: "timer").padding(.leading, 4) } compactTrailing: { Text(getTimeString(context.state.elapsedTime)).foregroundColor(.yellow) .padding(.trailing, 4) } minimal: { Image(systemName: "timer") .foregroundColor(.yellow) .padding(.all, 4) } .widgetURL(URL(string: "http://www.pushwoosh.com")) .keylineTint(Color.red) } } } ``` Don’t forget to also modify the `AppDelegate code`. **AppDelegate.swift** ```swift import UIKit import Flutter @main @objc class AppDelegate: FlutterAppDelegate { private let liveActivityManager: LiveActivityManager = LiveActivityManager() override func application( _ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? ) -> Bool { GeneratedPluginRegistrant.register(with: self) let controller : FlutterViewController = window?.rootViewController as! FlutterViewController let diChannel = FlutterMethodChannel(name: "PW", binaryMessenger: controller.binaryMessenger) diChannel.setMethodCallHandler({ [weak self] ( call: FlutterMethodCall, result: @escaping FlutterResult) -> Void in switch call.method { case "startLiveActivity": self?.liveActivityManager.startLiveActivity( data: call.arguments as? Dictionary, result: result) break case "updateLiveActivity": self?.liveActivityManager.updateLiveActivity( data: call.arguments as? Dictionary, result: result) break case "stopLiveActivity": self?.liveActivityManager.stopLiveActivity(result: result) break default: result(FlutterMethodNotImplemented) } }) return super.application(application, didFinishLaunchingWithOptions: launchOptions) } } ``` And that’s it! That covers everything needed to implement the Dynamic Island and link it to the Flutter code! ## Pushwoosh code implementation The Pushwoosh Flutter plugin provides two methods for managing Live Activity in the project. ```dart /// Default setup Live Activity Future defaultSetup() async { await _channel.invokeMethod("defaultSetup"); } /// Default start Live Activity /// [activityId] activity ID /// [attributes] attributes /// [content] content Future defaultStart(String activityId, Map attributes, Map content) async { await _channel.invokeMethod("defaultStart", {"activityId": activityId, "attributes": attributes, "content": content}); } ``` The `defaultSetup()` method allows you to manage the structure of Live Activity and tokens on the Pushwoosh side. Call this method during the application initialization. ```dart Pushwoosh.initialize({"app_id": "XXXXX-XXXXX"}); /** * Call this method `defaultSetup()` */ Pushwoosh.getInstance.defaultSetup(); ``` During the initialization of the app, Pushwoosh will send your push-to-start token to the server, which will later allow you to start the Live Activity via an API call. To start the Live Activity, you need to make an API request. Below is an example of the request: ```json { "request": { "application": "XXXXX-XXXXX", "auth": "YOUR_AUTH_API_TOKEN", "notifications": [ { "content": "Message", "title":"Title", "live_activity": { "event": "start", // `start` event "content-state": { "data": { // You need to pass the parameters in a dictionary with the key data. "emoji": "dynamic data" } }, "attributes-type": "DefaultLiveActivityAttributes", "attributes": { "data": { "name": "static data" } } }, "devices": [ "XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX" // HWID ], "live_activity_id": "your_activity_id" } ] } } ``` [More detailed documentation on the API](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api) If you want to start a simple Live Activity, for example, from your app, you can use the following method in Flutter: `defaultStart(String activityId, Map attributes, Map content)` This method can be called on any event that triggers the start of the Live Activity. ```dart // Function to start Live Activity void startLiveActivity() { // Create your activity ID String activityId = "stopwatch_activity"; // Define the attributes you want to send to the Live Activity Map attributes = { 'title': 'Stopwatch Activity', 'description': 'This is a live activity for a stopwatch.' }; // Define the content state to update on the Dynamic Island Map content = { 'elapsedSeconds': 0 }; // Call Pushwoosh's defaultStart method to trigger the Live Activity Pushwoosh.getInstance().defaultStart(activityId, attributes, content); } ``` --- # Flutter SDK Basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/integration/basic-integration-guide/ Learn how to integrate the Pushwoosh Flutter SDK, configure users, track events, set tags, and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh Flutter SDK into your application. ## Prerequisites To integrate the Pushwoosh Flutter SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Add Pushwoosh Flutter SDK Dependency Add the `pushwoosh_flutter` package to your `pubspec.yaml` file: pubspec.yaml ```yaml dependencies: flutter: sdk: flutter # Use the latest version from https://pub.dev/packages/pushwoosh_flutter pushwoosh_flutter: ^[LATEST_VERSION] ``` Check the [latest version](https://pub.dev/packages/pushwoosh_flutter) on pub.dev. Then, run the following command in your project’s root directory to install the dependency: ```bash flutter pub get ``` Double check that the package is installed correctly: ```bash flutter pub deps | grep pushwoosh_flutter # Example output: # ❯ flutter pub deps | grep pushwoosh_flutter # └── pushwoosh_flutter 2.3.11 ``` ### 2. Flutter SDK Initialization In the root component of your `main.dart` file: * Import the `pushwoosh_flutter` package. * Initialize the Pushwoosh SDK. * Call `registerForPushNotifications()` in your initialization logic to register for push notifications. main.dart ```dart import 'package:pushwoosh_flutter/pushwoosh_flutter.dart'; void main() async { runApp(const MyApp()); Pushwoosh.initialize({ "app_id": "__YOUR_APP_ID__" }); Pushwoosh.getInstance.registerForPushNotifications(); } ``` Where: * `__YOUR_APP_ID__` is the application code from the Pushwoosh Control Panel. ### 3. iOS Native Setup #### 3.1 Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. #### 3.2 Info.plist In your `Runner/Info.plist` set the `__PUSHWOOSH_DEVICE_API_TOKEN__` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): info.plist ```swift Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` #### 3.3 Message delivery tracking You must add a Notification Service Extension target to your project. This is essential for accurate delivery tracking and features like Rich Media on iOS. Follow the [native guide’s steps](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target and the necessary Pushwoosh code within it. To ensure, that Notification Service Extension is properly integrated in your Flutter project, you need to use the following Podfile configuration: Podfile ```ruby target 'NotificationServiceExtension' do use_frameworks! use_modular_headers! pod 'PushwooshXCFramework' inherit! :search_paths end ``` #### 3.4 Installing dependencies for the iOS Flutter project To install dependencies for the iOS Flutter project, run the following command: ```bash flutter run ``` or navigate to the `ios` folder in the terminal and run: ```bash pod install --repo-update ``` ### 4. Android Native Setup #### 4.1 Install dependencies Ensure that the required dependencies and plugins are added to your Gradle scripts: Add the Google Services Gradle plugin to your project-level `build.gradle` dependencies: android/build.gradle ```groovy buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' } } ``` Apply the plugin in your app-level `build.gradle` file: app/build.gradle ```groovy apply plugin: 'com.google.gms.google-services' ``` #### 4.2 Add Firebase configuration file Place the `google-services.json` file into `android/app` folder in your project directory. #### 4.3 Add Pushwoosh metadata In your `main/AndroidManifest.xml` add [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) inside the `` tag: AndroidManifest.xml ```xml ``` > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 5. Run the Project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notification event listeners In the Pushwoosh SDK there are two event listeners, designed for handling push notifications: * `onPushReceived` event is triggered, when a push notification is received * `onPushAccepted` event is triggered, when a user opens a notification You should set up these event listeners right after initialization of the SDK at the application start up: ```dart import 'package:pushwoosh_flutter/pushwoosh_flutter.dart'; class PushwooshNotificationHandler { void setupPushListeners(Pushwoosh pushwoosh) { pushwoosh.onPushReceived.listen((event) { print("Push received: ${event.pushwooshMessage.payload}"); }); pushwoosh.onPushAccepted.listen((event) { print("Push accepted: ${event.pushwooshMessage.payload}"); }); } } ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty ```dart import 'package:pushwoosh_flutter/pushwoosh_flutter.dart'; class Registration { void afterUserLogin(User user) { // Set user ID Pushwoosh().setUserId(user.getId()); // Set user email Pushwoosh().setEmail(user.getEmail()); // Register SMS number // The SMS and WhatsApp numbers must be in E.164 format (e.g., "+1234567890") and be valid Pushwoosh().registerSmsNumber(user.getSmsNumber()); // Register WhatsApp number Pushwoosh().registerWhatsappNumber(user.getWhatsappNumber()); // Setting additional user information as tags for Pushwoosh Pushwoosh().setTags({ "age": user.getAge(), "name": user.getName(), "last_login": user.getLastLoginDate() }); } } ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. ```dart import 'package:pushwoosh_flutter/pushwoosh_flutter.dart'; class UpdateUser { void afterUserUpdateProfile(User user) { // Set list of favorite categories Pushwoosh().setTags({ "favorite_categories": user.getFavoriteCategoriesList() }); // Set payment information Pushwoosh().setTags({ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() }); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions ```dart import 'package:pushwoosh_flutter/pushwoosh_flutter.dart'; class Registration { // Track login event void afterUserLogin(User user) { Pushwoosh().postEvent("login", { "name": user.getName(), "last_login": user.getLastLoginDate() }); } void afterUserPurchase(Product product) { // Track purchase event Pushwoosh().postEvent("purchase", { "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() }); } } ``` ## Using ProGuard Note Note that the `flutter build apk` command obfuscates your code by default. Thus, you may get this exception: ```java java.lang.IllegalStateException: Could not find class for name: com.pushwoosh.plugin.PushwooshNotificationServiceExtension ``` There are two solutions in this case: 1. Use the `flutter build apk --no-shrink` command to compile your code without obfuscation. 2. Or you can manually enable ProGuard and add the necessary rules. To enable ProGuard for your project, add the following strings to your `build.gradle` file: build.gradle ```java buildTypes { release { minifyEnabled true useProguard true proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' signingConfig signingConfigs.debug } } ``` Then, add the following rules to the `android/app/proguard-rules.pro` proguard-rules.pro ```java #Pushwoosh Flutter -keep class com.pushwoosh.plugin.PushwooshPlugin { *; } -keep class com.pushwoosh.plugin.PushwooshNotificationServiceExtension { *; } ``` ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # Flutter SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/integration/quick-start/ Get started with the Pushwoosh Flutter SDK using this quick and simple example. --- Get started with the Pushwoosh Flutter SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh Flutter SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project ```bash # Clone the SDK repository and navigate to the example project git clone https://github.com/Pushwoosh/pushwoosh-flutter # Navigate to example directory cd pushwoosh-flutter/Source/pushwoosh/example ``` ## Configure the project Install the dependencies: ```bash flutter pub get ``` Open **lib/main.dart** and update the Pushwoosh.initialize call: ```dart Pushwoosh.initialize({"app_id": "XXXXX-XXXXX"}); ``` Replace the `app_id` value with your **Pushwoosh App Code**. ### iOS ###### 1. Install dependencies Navigate to the ios directory and install the dependencies: ```bash cd ios && pod install --repo-update ``` ###### 2. Set the Bundle Identifier Open the **ios/Runner.xcworkspace** file in Xcode. Set the **Bundle Identifier** for each target: * `Runner`: your app’s package name. (eg. com.example.pushwoosh) * `NotificationService`: your app’s package name with `.NotificationServiceExtension` (eg. com.example.pushwoosh.NotificationServiceExtension) * `StopwatchWidgetExtension`: your app’s package name with `.StopwatchWidget` (eg. com.example.pushwoosh.StopwatchWidget) ###### 3. Set the API Token In your `ios/Runner/Info.plist` set the `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### Android ###### 1. Setup google-services.json Copy your `google-services.json` file to the **android/app** directory ###### 2. Set the package name Open **android/app/build.gradle** and set the **applicationId** to your app’s package name. (eg. `com.example.pushwoosh`) ###### 3. Set the API Token Open your **android/app/src/main/AndroidManifest.xml** file and add the following metadata inside the `` tag: AndroidManifest.xml ```xml ``` Replace `__YOUR_PUSHWOOSH_DEVICE_API_TOKEN__` with your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). ## Run the project 1. Build and run the project. 2. The app will have two tabs: “Actions” and “Settings”. Navigate to the **Settings** tab. 3. Tap the **Register for Pushes** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Known issues when migrating Pushwoosh Flutter Plugin to v2.1.0+ **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/known-issues-migrating-pushwoosh-flutter-plugin-to-version-210-or-higher/ Find solutions to common build errors when migrating Pushwoosh Flutter Plugin to version 2.1.0 or higher, including dependency conflicts and SDK version mismatches. --- If you are facing errors building your Android application after updating Pushwoosh Flutter Plugin to version 2.1.0 or higher, please search this guide for the error you encountered and follow the instructions to fix it. ### Build error: Google Play services dependency conflict `In project 'app' a resolved Google Play services library dependency depends on another at an exact version (e.g. "[17.0.0, 18.0.99]", but isn't being resolved to that version. Behavior exhibited by the library will be unknown.` #### Solution: Add this line to your **app/build.gradle** file: ```plaintext googleServices { disableVersionCheck = true } ``` ### Build error: minCompileSdk Mismatch `The minCompileSdk (30) specified in a dependency's AAR metadata (META-INF/com/android/build/gradle/aar-metadata.properties) is greater than this module's compileSdkVersion (android-29).` #### Solution: Update compileSdkVersion in your **app/build.gradle** file: ```plaintext android { compileSdkVersion 30 ... ``` ### Build error: Unexpected **queries** element in Manifest `AAPT: error: unexpected element found in .` #### Solution: Update gradle plugin in your project. To do this, open **PROJECT_DIR/build.gradle** and update gradle plugin version to version 4 or higher: ```plaintext classpath 'com.android.tools.build:gradle:4.1.2' ``` In **PROJECT_DIR/gradle/wrapper/gradle-wrapper.properties**, update gradle wrapper version: ```plaintext distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip ``` ### Build error: Missing transform input file After updating gradle plugin to version 4 or higher, you may also face a known build error `Transform's input file does not exist: /build/app/intermediates/flutter/debug/libs.jar.` #### Solution: Change this in your **app/build.gradle** file ```plaintext lintOptions { disable 'InvalidPackage' } ``` to this: ```plaintext lintOptions { checkReleaseBuilds false } ``` --- # Setting up badges for Flutter **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/setting-up-badges-for-flutter/ Learn how to implement badges for the Pushwoosh Flutter plugin by setting up NotificationServiceExtension and configuring App Groups. --- 1. Open your iOS project located in **your_project/ios/Runner.xcworkspace** and create **NotificationServiceExtension**: 2. Open Podfile (located in **your_project/ios/Podfile**) and add **PushwooshXCFramework** dependency for NotificationServiceExtensionTarget: ```plaintext target ‘NotificationService’ do pod ‘PushwooshXCFramework’, ‘>=6.5.0’ end ``` Note You can put these lines after `target ‘Runner’ do` like in the example below: Podfile example ```bash target ‘Runner’ do flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) end target ‘NotificationService’ do pod ‘PushwooshXCFramework’, ‘>=6.5.0’ end post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end end ``` 3. Install pods via Terminal: ```plaintext cd ios && pod install ``` 4. Close and reopen your Xcode project. Caution Make sure that you open the project **via .xcworkspace** and not via .xcproject. 5. Make sure that your **Deployment target matches the one in the Runner target**; otherwise, you might face an issue when building your app (e.g., if you specify iOS 10.0 in Runner and iOS 15.5 in NotificationService targets). 6. Add **App Groups capability** to both Runner and NotificationService targets and add a new group with the same name for both targets: 7. Add **PW_APP_GROUPS_NAME** info.plist flag to both Runner and NotificationService targets with the group name as its string value: 8. Go to **NotificationService.m** and replace its code with the following: ```objective-c #import “NotificationService.h” #import <Pushwoosh/PWNotificationExtensionManager.h> @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your “best attempt” at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Setting up Message Inbox for Flutter **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/setting-up-message-inbox-for-flutter/ Show the Message Inbox screen in a Flutter app, style it with PWInboxStyle, and manage messages using the pushwoosh_inbox package. --- Message Inbox (the “bell icon” App Inbox) for Flutter ships as a separate `pushwoosh_inbox` package, install it alongside the base `pushwoosh_flutter` package. ## Prerequisites * The [base Pushwoosh Flutter SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/flutter/integration/basic-integration-guide/) already integrated and sending pushes. ## Add the package Add `pushwoosh_inbox` to your `pubspec.yaml`. Check [pub.dev](https://pub.dev/packages/pushwoosh_inbox) for the current version: pubspec.yaml ```yaml dependencies: pushwoosh_inbox: '^2.3.24' ``` ## Show the inbox ```dart import 'package:pushwoosh_inbox/pushwoosh_inbox.dart'; PushwooshInbox.presentInboxUI(); ``` This opens a native screen (a view controller on iOS, an Activity on Android) with the SDK’s default appearance. ## Customize the style Pass a `PWInboxStyle` to `presentInboxUI()`. Every field is optional and keeps its default value when left unset. Image fields are Flutter asset paths and must be declared in your `pubspec.yaml`: | Field | Customizes | | ---------------------- | ------------------------------------------------------------------ | | `dateFormat` | Date formatting, e.g. `"dd.MMMM.yyyy"` | | `defaultImage` | The default icon next to a message with no image | | `unreadImage` | The unread-messages mark (iOS only) | | `listErrorImage` | Shown when loading the list fails | | `listEmptyImage` | Shown when the list has no messages | | `listErrorMessage` | Error text (not localized) | | `listEmptyMessage` | Empty-list text (not localized) | | `barTitle` | The inbox screen’s title | | `defaultTextColor` | Default text color (iOS only) | | `accentColor` | Accent color | | `backgroundColor` | Background color | | `highlightColor` | Selection color | | `titleColor` | Message title color | | `readTitleColor` | Title color for read messages (Android only) | | `descriptionColor` | Message description color | | `readDescriptionColor` | Description color for read messages (Android only) | | `dateColor` | Message date color | | `readDateColor` | Date color for read messages (Android only) | | `dividerColor` | Separator color | | `barBackgroundColor` | The title bar’s background color | | `barAccentColor` | The title bar’s back-button color | | `barTextColor` | The title bar’s text color | | `imageTypeColor` | The unread action icon color, e.g. deep link or URL (Android only) | | `readImageTypeColor` | The read action icon color (Android only) | ```dart PWInboxStyle style = PWInboxStyle(); style.barTitle = "Messages"; style.barBackgroundColor = "#3AD29F"; style.accentColor = "#6750A4"; style.listEmptyMessage = "No messages yet"; style.defaultImage = "assets/images/logo.png"; PushwooshInbox.presentInboxUI(style: style); ``` ## Manage messages programmatically Load the message list, read counts, and act on individual messages without opening the built-in screen. This is useful for a custom inbox UI or an unread badge: ```dart try { List messages = await PushwooshInbox.loadMessages(); for (final message in messages) { print("${message.title}: ${message.message}"); } int? unread = await PushwooshInbox.unreadMessagesCount(); int? total = await PushwooshInbox.messagesCount(); int? notActedOn = await PushwooshInbox.messagesWithNoActionPerformedCount(); } catch (error) { print("Failed to load inbox data: $error"); } PushwooshInbox.readMessage(messageCode); PushwooshInbox.deleteMessage(messageCode); PushwooshInbox.performAction(messageCode); // runs the message's default action, e.g. opening a URL ``` `loadCachedMessages()` returns the last locally cached list without a network call on Android. On iOS it currently behaves the same as `loadMessages()` and still calls the network. --- # MAUI Dotnet **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/ Integrate the Pushwoosh SDK into a .NET 8 or .NET 10 MAUI app and start receiving push notifications on Android and iOS devices --- * [**Module source**](https://github.com/Pushwoosh/pushwoosh-dotnet) * [**Download sample**](https://github.com/Pushwoosh/pushwoosh-dotnet/tree/main/Sample) ### Requirements To integrate the SDK, your MAUI project needs the following package version, target frameworks, and minimum OS versions: * `Pushwoosh.DotNet` 2.1.0 or later. The package ships builds for .NET 8 and .NET 10 (`net10.0-android36.0` and `net10.0-ios26.0`). * Android: target framework `net8.0-android34.0` or higher, devices with API 23 or later. Plain `net8.0-android` resolves to `android34.0` on the .NET 8 SDK. * iOS: devices with iOS 15.0 or later. Set the minimum in your project file: ```xml 15.0 ``` With a lower value, the build stops with `error PW0001`. ### SDK integration **1.** Add **Pushwoosh.DotNet** Nuget package to both Android and iOS projects: **2.** Accept licenses for transitive Android dependencies if required: **3.** Configure your project in [Firebase Console](https://console.firebase.google.com/u/0/). **4.** Add **google-services.json** to the root of your Android platform directory. Set **GoogleServicesJson** build action for this file: **5.** Add the following line before declaring namespace in your **MainApplication.cs** (**XXXXX-XXXXX** here is your Pushwoosh Application Code): ```csharp [assembly: MetaData("com.pushwoosh.appid", Value = "XXXXX-XXXXX")] ``` **6.** In your **MainApplication.cs**, override `OnCreate()` and call `PushManager.Init();` before `base.OnCreate();`: ```csharp using Android.App; using Android.Runtime; using Pushwoosh.Android; ... [Application] public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } public override void OnCreate() { PushManager.Init(); base.OnCreate(); } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } ``` `base.OnCreate()` builds the MAUI app and constructs `App`, which already uses `PushManager.Instance`. Don’t call `PushManager.Init()` from `MainActivity.OnCreate`. The activity is created after `App`, so `PushManager.Instance` would still be `null` when `App` needs it. **7.** In your `AndroidManifest.xml`, add the following: ```xml ``` > **Important:** Make sure the [Device API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) has access to the correct application in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) **8.** In the iOS platform folder, create **Platforms/iOS/Entitlements.plist** with the `aps-environment` key: ```xml aps-environment development ``` Without this entitlement, iOS doesn’t issue an APNs device token, so `Register()` always fails. Use `development` for debug builds. A distribution build needs `production` and a provisioning profile with push notifications enabled. **9.** In your project file (**.csproj**), point the iOS target to the entitlements file: ```xml Platforms/iOS/Entitlements.plist ``` **10.** In the **AppDelegate.cs** of your iOS project, call `PushManager.Init();` at the beginning of the `FinishedLaunching` method: ```csharp using Foundation; using UIKit; using Pushwoosh.iOS; ... public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { PushManager.Init(); return base.FinishedLaunching(application, launchOptions); } ``` **11.** Add these methods to the **AppDelegate**. The `MauiUIApplicationDelegate` class doesn’t export them by default, so add them manually: ```csharp [Export("application:didReceiveRemoteNotification:fetchCompletionHandler:")] public void DidReceiveRemoteNotification(UIKit.UIApplication application, NSDictionary userInfo, Action completionHandler) { PushManager.Instance.ReceivedRemoteNotification(userInfo); } [Export("application:didRegisterForRemoteNotificationsWithDeviceToken:")] public void RegisteredForRemoteNotifications(UIKit.UIApplication application, NSData deviceToken) { PushManager.Instance.RegisteredForRemoteNotifications(deviceToken); } [Export("application:didFailToRegisterForRemoteNotificationsWithError:")] public void FailedToRegisterForRemoteNotifications(UIKit.UIApplication application, NSError error) { PushManager.Instance.FailedToRegisterForRemoteNotifications(error); } ``` **12.** In the **Info.plist**, change the **Pushwoosh_APPID** value to your Pushwoosh Application Code. You can find it in your Control Panel: ![Info.plist in Xcode with the Pushwoosh_APPID key set to the application code](https://docs.pushwoosh.com/maui-dotnet-README-4.webp) **13.** In the **Info.plist**, set the `Pushwoosh_API_TOKEN` key to your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) **14.** In the cross-platform part of **App.xaml.cs**, call `Register()` in the `OnStart` method. It asks the user for notification permission and registers the device for push notifications: ```csharp using PushwooshSDK.DotNet.Core; ... public partial class App : Application { ... protected override void OnStart() { base.OnStart(); PushManager.Instance.Register(); } } ``` ### Upgrade from 2.0.x to 2.1.0 If your app was set up with an earlier version of this guide, make these changes when you update to 2.1.0: * On Android, move `PushManager.Init()` from `MainActivity.OnCreate` to `MainApplication.OnCreate`, before `base.OnCreate()`, as in [step 6](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/#sdk-integration). If you use Geozones, move `LocationManager.Init()` there too, right after `PushManager.Init()`. * Remove the `com.pushwoosh.senderid` metadata line and the `fcm_sender_id` string from **Resources/values/strings.xml**. The SDK doesn’t read them. * On iOS, add the `aps-environment` entitlement from steps 8 and 9 if your app doesn’t have it yet. * Raise `SupportedOSPlatformVersion` for the iOS target to 15.0, as shown in [Requirements](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/#requirements). The MAUI 8 project template sets 11.0. * If your app uses [Geozones](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/maui-dotnet-geozones-integration/), update `Pushwoosh.DotNet.Geozones` to 2.1.0 together with `Pushwoosh.DotNet`. With Geozones 2.0.x, the build stops with `error PW0002`. * `PWInlineInAppView` on iOS is marked `[Obsolete]` and shows nothing, because the native iOS SDK 7.x has no inline in-app messages. Remove it from your layouts. > **Note for Windows users:** When building you may encounter an issue related to the length of the file path. This is due to the combined length of the directories used by Visual Studio and the file paths within the Pushwoosh SDK exceeding Windows >maximum path length limitation of 260 characters. To resolve this issue, enable the LongPathsEnabled setting in the Windows Registry. ### Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # MAUI Dotnet Geozones integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/maui-dotnet-geozones-integration/ Learn how to integrate geozones functionality into your MAUI .NET application --- Geozones let Pushwoosh send push notifications based on the device location. Before you start, integrate the base SDK as described in [MAUI Dotnet](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/), including its [requirements](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/maui-dotnet/#requirements). Then follow these steps: 1. Add the Pushwoosh.DotNet.Geozones NuGet Package to your solution. Update it together with `Pushwoosh.DotNet`. Geozones 2.0.x crashes at launch on iOS next to `Pushwoosh.DotNet` 2.1.0 or later, so with that pair the build stops with `error PW0002`. 2. Android integration In your Android project, locate the MainApplication.cs file and call `LocationManager.Init()` in `OnCreate()` right after `PushManager.Init()`, before `base.OnCreate()`. `base.OnCreate()` builds the MAUI app, and a page that uses `LocationManager.Instance` while it’s constructed finds it `null` if `Init()` runs later: ```csharp using Android.App; using Android.Runtime; using Pushwoosh.Android; using PushwooshSDK.DotNet.Geozones.Android; ... [Application] public class MainApplication : MauiApplication { public MainApplication(IntPtr handle, JniHandleOwnership ownership) : base(handle, ownership) { } public override void OnCreate() { PushManager.Init(); LocationManager.Init(); base.OnCreate(); } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } ``` 3. iOS integration For your iOS project, open the AppDelegate.cs file and call `LocationManager.Init()` in `FinishedLaunching()`method: ```csharp using Pushwoosh.iOS; using PushwooshSDK.DotNet.Geozones.iOS; using UIKit; ... [Register("AppDelegate")] public class AppDelegate : MauiUIApplicationDelegate { public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions) { PushManager.Init(); LocationManager.Init(); return base.FinishedLaunching(application, launchOptions); } protected override MauiApp CreateMauiApp() => MauiProgram.CreateMauiApp(); } ``` 4. Add location permissions based on your app’s requirements. **Privacy - Location When In Use Usage Description** permission should always be added. 5. Then start location tracking by calling the `StartLocationTracking()`method. ```csharp using PushwooshSDK.DotNet.Geozones; ... LocationManager.Instance.StartLocationTracking(); ``` Note Remember to test your implementation on both Android and iOS platforms to ensure everything is working as expected. --- # Mendix **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/ Add push notifications to a Mendix native mobile app with the Pushwoosh module: one widget, nanoflow actions, no native code. --- The Pushwoosh module for Mendix adds push notifications and customer engagement to a Mendix native mobile app without writing native code. It is published on the [Mendix Marketplace](https://marketplace.mendix.com/), installs from inside Studio Pro, and exposes the Pushwoosh SDK as one pluggable widget plus a set of JavaScript actions you call from nanoflows. ## What you get * Push notifications on iOS and Android, in the foreground and in the background, including deep links. * Nanoflow events for a notification arriving, a notification being opened, and a deep link being followed. * Tags and segmentation, user identity, events for triggered campaigns, local notifications, a consent toggle, and SMS and WhatsApp registration. The native libraries are installed by Mendix during the native build and pinned to the exact version the module was tested with, so you never open Xcode or Gradle to set this up. ## Requirements | | | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | Studio Pro | 11.12.3 or newer, native mobile navigation profile | | Platforms | iOS and Android | | Pushwoosh account | A [Pushwoosh account](https://sso.pushwoosh.com/login) and an [Application Code](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) | | Build type | A custom developer app or a real native build. The Make It Native app cannot run third-party native code | ## Choose your integration path * [Quick start](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/quick-start/) - run the ready-made demo app and send your first push in a few minutes * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/basic-integration-guide/) - add the module to your own app step by step * [Module actions reference](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-module-actions/) - the widget properties and the JavaScript actions, with parameters and return values * [Mendix FAQ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-faq/) - the questions that come up during the first integration ## Where the source code lives The module is open source under the MIT license: [github.com/Pushwoosh/pushwoosh-mendix](https://github.com/Pushwoosh/pushwoosh-mendix). The repository holds the widget, the actions, the demo app, and the changelog for every released version. --- # Mendix module basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/basic-integration-guide/ Add the Pushwoosh module to a Mendix native mobile app: install it, register devices, handle notifications, set tags and post events. --- This guide walks through adding the Pushwoosh module to an existing Mendix native mobile app, from installing it out of the Marketplace to receiving a notification on a device. ## Prerequisites To integrate the Pushwoosh module into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. The Device API Token in the list above is not needed for this module. The widget takes the Application Code and nothing else. You will also need the following: * Mendix Studio Pro 11.12.3 or newer. The module package is built with that version, and Studio Pro cannot open a package produced by a newer version than itself. * A native mobile navigation profile in your app. * Xcode or Android Studio to produce the native build. The Make It Native app cannot run this module, because it does not contain third-party native code. ## 1. Install the module from the Marketplace In Studio Pro, click the Marketplace icon in the top right corner, search for **Pushwoosh**, and click **Download**. Studio Pro adds a module named `Pushwoosh` to your app. ![The Marketplace panel in Studio Pro with one search result for Pushwoosh](https://docs.pushwoosh.com/mendix-basic-integration-1.webp) There is nothing else to install. The native libraries come from npm during the native build, pinned to the exact version the module was tested with. ## 2. Add the Pushwoosh events widget to your home page The **Pushwoosh events** widget starts the SDK and turns push notifications into nanoflow events. It renders nothing, so place it once, on the page that loads first in the native mobile navigation profile. 1. Open the home page of your native mobile navigation profile. 2. Drag the **Pushwoosh events** widget onto the page. It is in the **Pushwoosh** category of the toolbox. 3. Set the **Application Code** property to the Application Code of your app from the Pushwoosh Control Panel. It looks like `XXXXX-XXXXX`. ![The Page Explorer in Studio Pro showing the Pushwoosh events widget on the home page](https://docs.pushwoosh.com/mendix-basic-integration-2.webp) Leave **Register on load** enabled to register the device as soon as the app opens. Turn it off if you would rather ask for notification permission later, at a point in the app where the request makes more sense to the user. See [Register the device](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/basic-integration-guide/#4-register-the-device) below. ## 3. Configure the native build Pushwoosh replaces the built-in push notifications of a Mendix app rather than working next to them. In the Native Mobile Builder, set the capabilities as follows: * **Push notifications**: off. * **Firebase Android**: on, so that `google-services.json` is still picked up. Two notification services registered for the `com.google.firebase.MESSAGING_EVENT` event shadow each other, and notifications stop arriving. On Android, the FCM Sender ID comes from `google-services.json`; it is not passed to the module. On iOS, add the **Push Notifications** capability to the generated Xcode project in **Signing & Capabilities**, and upload your APNs key to the Pushwoosh Control Panel as described in the [iOS platform configuration guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/). Building on macOS Studio Pro for Mac has no Native Mobile Builder. Configure the native template with the [`@mendix/native-mobile-toolkit`](https://www.npmjs.com/package/@mendix/native-mobile-toolkit) command line tool instead, then build the generated iOS and Android projects locally with Xcode and Gradle. ## 4. Register the device A device receives notifications only after it is registered with Pushwoosh. There are two ways to register, and you need one of them. **Register on load.** Leave the **Register on load** property of the widget enabled. The device registers as soon as the widget loads, and on iOS the system permission prompt appears at that moment. **Register from a nanoflow.** Turn **Register on load** off and call the `RegisterForPushNotifications` action from a nanoflow of your own, for example after the user signs in or accepts a consent screen. The action returns the push token as a string. ![A nanoflow calling RegisterForPushNotifications and showing the returned push token](https://docs.pushwoosh.com/mendix-basic-integration-4.webp) To stop delivery to a device, call `UnregisterForPushNotifications`. ## 5. Handle a notification that arrives while the app is open When a notification arrives while the app is in the foreground, the widget writes the notification into the attributes you point it at, and then triggers the nanoflow in **On push received**. 1. Set **Message** to a string attribute that receives the notification text. 2. Set **Payload** to a string attribute that receives the full payload as JSON. 3. Set **On push received** to a nanoflow that reacts to it, for example one that shows a message or refreshes a list. The attributes are filled before the nanoflow runs, so the nanoflow can read them straight away. ![The Push events properties of the widget: Payload, Message, On push received and On push opened](https://docs.pushwoosh.com/mendix-basic-integration-5.webp) ## 6. Handle an opened notification and a deep link When the user taps a notification, the widget triggers the nanoflow in **On push opened**, with the same **Message** and **Payload** attributes filled. If the notification carries a deep link, the widget also writes the link into the attribute set in **Deep link** and triggers the nanoflow in **On deep link**. That nanoflow runs in addition to **On push opened**, so use it to route the user to the page the link points at. ## 7. Identify the user and set tags Tags are what turn a broadcast into a targeted message. Call `SetTags` from any nanoflow with a JSON object as its parameter: ```plaintext '{"Language":"en","Plan":"pro","Level":7}' ``` Quotes in Mendix expressions A string in a Mendix expression is written in single quotes, so the JSON inside it keeps its double quotes unescaped. Writing `""` or `\"` inside the expression produces a string the action cannot parse. Call `SetUserId` with your own user identifier to associate the device with a person rather than with a device. All devices registered under the same user ID can then be reached with one message. ![A nanoflow calling SetTags with the tag key and value typed in on the device](https://docs.pushwoosh.com/mendix-basic-integration-6.webp) ## 8. Post events Events drive triggered campaigns and in-app messages. Call `PostEvent` with the event name and, optionally, a JSON object of attributes: ```plaintext '{"product":"Coffee grinder","price":129}' ``` The event name must match the event configured in the Pushwoosh Control Panel. ## 9. Send a test notification Register the device as a test device and send a message to it from the Pushwoosh Control Panel. See [Test your integration](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/) for the steps. A notification that arrives while the app is open is shown by the system as an alert, and the **On push received** nanoflow runs as well. To suppress the system alert and handle the notification only in the nanoflow, call `SetShowForegroundAlert` with `false`. That action works on iOS only. ## 10. Check the integration when nothing arrives Work through these in order when the device receives no notifications: 1. Confirm the app is a custom developer app or a real native build. The Make It Native app does not contain the module, so every Pushwoosh action fails silently in it. 2. Confirm the capabilities of the native build: **Push notifications** off, **Firebase Android** on. 3. Confirm the device is registered. Call `GetPushToken` and check that it returns a token rather than an empty string. 4. Confirm the platform is configured in the Pushwoosh Control Panel, with the APNs key uploaded for iOS and the Firebase server key for Android. If the device is still not reachable, call `GetHwid` and open a support ticket with that identifier. ## Next steps * [Module actions reference](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-module-actions/) - the widget properties and the JavaScript actions, with parameters and return values * [Mendix FAQ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-faq/) - the questions that come up during the first integration --- # Mendix module quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/quick-start/ Run the Pushwoosh demo app for Mendix and send your first push notification to a device in a few minutes. --- Get started with the Pushwoosh module for Mendix by running the demo app that ships with the module and sending a push notification to it. ## Prerequisites To run the demo app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. You will also need Mendix Studio Pro 11.12.3 or newer with a native mobile navigation profile, and Xcode or Android Studio to produce the native build. The Device API Token in the list above is not needed for this module. The widget takes the Application Code and nothing else. ## Download the demo app Clone the module repository: ```bash git clone https://github.com/Pushwoosh/pushwoosh-mendix.git ``` The demo app is the `demo` folder. It is the same app the module is developed against: one screen with buttons for registering and unregistering the device, reading the push token, setting and reading tags, and posting an event, plus inputs for the tag key and value and for the event name. ## Open the app in Studio Pro Open `demo/PushwooshDemo.mpr` in Studio Pro. The app already contains the `Pushwoosh` module, the **Pushwoosh events** widget on the home page, and a nanoflow behind every button. ![The App Explorer in Studio Pro showing the Pushwoosh module and its JavaScript actions alongside the demo nanoflows](https://docs.pushwoosh.com/mendix-quick-start-1.webp) ## Set your Application Code Open the home page of the native mobile navigation profile, select the **Pushwoosh events** widget, and replace the `XXXXX-XXXXX` placeholder in the **Application Code** property with the Application Code of your app from the Pushwoosh Control Panel. ![The Setup properties of the Pushwoosh events widget, with the Application Code filled in](https://docs.pushwoosh.com/mendix-quick-start-2.webp) ## Build the native app The demo cannot run in the Make It Native app, because that app does not contain third-party native code. Produce a custom developer app or a real native build instead. On Windows, use **App** > **Build Native App** to open the Native Mobile Builder, and set the capabilities as follows: * **Push notifications**: off. * **Firebase Android**: on. Pushwoosh replaces the built-in push of a Mendix app rather than working next to it. Two notification services registered for the same Firebase event shadow each other and notifications stop arriving. On macOS, Studio Pro has no Native Mobile Builder. Configure the native template with the [`@mendix/native-mobile-toolkit`](https://www.npmjs.com/package/@mendix/native-mobile-toolkit) command line tool instead, then build the generated iOS and Android projects locally. For iOS, open the generated Xcode project and add the **Push Notifications** capability in **Signing & Capabilities**. For Android, place the `google-services.json` file from your Firebase project into the generated Android project. ## Run the app and register the device Start the Mendix runtime for the app, then launch the native build on a device or a simulator and tap **Register For Push Notifications**. The device asks for notification permission, registers with Pushwoosh, and shows the push token in a message. ## Send a test push notification Register the device as a test device and send a message to it from the Pushwoosh Control Panel. See [Test your integration](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/) for the steps. ## Next steps * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/integration/basic-integration-guide/) - add the module to your own app * [Module actions reference](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-module-actions/) - the widget properties and the JavaScript actions --- # Mendix FAQ **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-faq/ Answers to the questions that come up when integrating the Pushwoosh module into a Mendix native mobile app. --- ## Which Studio Pro version does the Pushwoosh module require? Studio Pro 11.12.3 or newer. The module package is built with that version of the model format, and Studio Pro refuses to open a package produced by a version newer than itself. There is a second reason not to go lower. Mendix moves Studio Pro, the native template and React Native together, so the Studio Pro version decides which React Native version your app runs on, and the module is built against React Native 0.84. The authoritative mapping is [`mendix_version.json`](https://github.com/mendix/native-template/blob/master/mendix_version.json) in the native template repository. ## Why does nothing happen after I add the module? The app is most likely running in the Make It Native app, which cannot contain third-party native code, so every Pushwoosh action fails silently. Build a custom developer app or a real native build instead. If the app is already a native build, check that the **Pushwoosh events** widget is on the page that loads first and that its **Application Code** property is filled in. Without a valid Application Code the SDK does not start. ## Why do notifications stop arriving on Android? Two notification services are registered for the same Firebase event and shadow each other. Pushwoosh replaces the built-in push notifications of a Mendix app rather than working next to them. In the Native Mobile Builder, set the capabilities as follows: * **Push notifications**: off. * **Firebase Android**: on, so that `google-services.json` is still picked up. Only one service may be registered for `com.google.firebase.MESSAGING_EVENT`. If your app contains another plugin that also registers one, see [Using Pushwoosh SDK with other FCM services](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#using-pushwoosh-sdk-with-other-fcm-services). ## Do I need to call Initialize when I use the Pushwoosh events widget? No. The widget initializes the SDK itself from its **Application Code** property. The `Initialize` action exists for apps that start the SDK from a nanoflow instead of placing the widget, for example when the Application Code is not known until the user has signed in. Calling both is harmless, but the widget has already done the work by the time your nanoflow runs. ## How do I pass JSON to SetTags or PostEvent from a Mendix expression? Write the JSON in single quotes, so the double quotes inside it stay unescaped: ```plaintext '{"Language":"en","Level":7}' ``` A string in a Mendix expression is delimited by single quotes, which leaves the double quotes of the JSON free to be used as they are. Doubling them or escaping them with a backslash produces a string the action cannot parse, and the action rejects it with a parse error. ## How do I stop the system alert while the app is open? By default a notification that arrives while the app is open is shown by the system as an alert, and the **On push received** nanoflow of the widget runs as well. Call the `SetShowForegroundAlert` action with `false` to suppress the alert and handle the notification only in the nanoflow, for example by showing a message of your own or refreshing a list. Call that action on iOS only. The Android SDK does not expose the setting, and the JavaScript wrapper calls the native method unconditionally, so on Android the action raises an error rather than doing nothing. ## Can I build the native app with Studio Pro for Mac? Not with the Native Mobile Builder, which ships only with the Windows version of Studio Pro. Everything else works on the Mac build, including writing the module, placing the widget and exporting the app. On macOS, configure the native template with the [`@mendix/native-mobile-toolkit`](https://www.npmjs.com/package/@mendix/native-mobile-toolkit) command line tool, which sets the capabilities, the runtime URL, the app identifier and the native dependencies from a configuration file. Then build the generated iOS and Android projects locally with Xcode and Gradle. ## How do I send a test notification to one specific device? Call the `GetHwid` action to get the [HWID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) of the device, and address that HWID from the Pushwoosh Control Panel or the [server API](https://docs.pushwoosh.com/developer/api-reference/messages-api/). Registering the device as a test device is the other way to do it, described in [Test your integration](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/). --- # Pushwoosh Mendix module actions **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/mendix/mendix-module-actions/ Reference for the Pushwoosh events widget properties and the JavaScript actions the Mendix module exposes to nanoflows. --- The Pushwoosh module for Mendix exposes its functionality in two places: the **Pushwoosh events** widget, which starts the SDK and turns incoming notifications into nanoflow events, and the JavaScript actions you call from any nanoflow on the native mobile navigation profile. This page lists both. ## Pushwoosh events widget properties The widget renders nothing. Place it once, on the page that loads first in the native mobile navigation profile. It initializes the SDK, optionally registers the device, and triggers a nanoflow for each push notification event. ![The properties pane of the Pushwoosh events widget, with the Setup, Push events and Deep links groups](https://docs.pushwoosh.com/mendix-module-actions-1.webp) ### Setup These properties start the SDK and report when it is ready. | Property | What it does | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Application Code** | The Pushwoosh Application Code of your app, for example `XXXXX-XXXXX`. Required. | | **Register on load** | Registers the device as soon as the widget loads. On iOS this shows the system permission prompt, so turn it off if you would rather ask later with the `RegisterForPushNotifications` action. | | **On initialized** | A nanoflow that runs once the SDK has started. | | **On registered** | A nanoflow that runs when the device has a push token. | | **Push token** | The string attribute the push token is written to before **On registered** runs. | ### Push events These properties deliver an incoming notification into your model and trigger a nanoflow. | Property | What it does | | -------------------- | ------------------------------------------------------------------------------------ | | **Payload** | The string attribute that receives the full notification payload as JSON. | | **Message** | The string attribute that receives the notification text. | | **On push received** | A nanoflow that runs when a notification arrives while the app is in the foreground. | | **On push opened** | A nanoflow that runs when the user taps a notification. | ### Deep links These properties handle a notification that carries a link into a specific page of the app. | Property | What it does | | ---------------- | ----------------------------------------------------------------------------------------------------------- | | **Deep link** | The string attribute that receives the deep link URL from the notification. | | **On deep link** | A nanoflow that runs when an opened notification carries a link. It runs in addition to **On push opened**. | The attributes are filled before the nanoflow runs, so the nanoflow can read them straight away. ## Setup actions Use these actions when you start the SDK yourself instead of using the widget. ### Initialize Initializes the Pushwoosh SDK. Call it once, before any other Pushwoosh action, typically from the on-load nanoflow of the home page. You do not need it when the **Pushwoosh events** widget is on the page, because the widget initializes the SDK itself. On Android, the FCM Sender ID comes from `google-services.json`, which the native build picks up. It is not passed to this action. | Parameter | Type | Description | | ---------------------- | ------ | ------------------------------------------------------------------------- | | `applicationCode` | String | The Pushwoosh Application Code, for example `XXXXX-XXXXX`. Required. | | `notificationHandling` | String | Optional. `default` or `manual`. Leave it empty for the default behavior. | Returns a Boolean that is true when the SDK started. ## Registration actions These actions control whether this device receives notifications, and expose the identifiers that address it. ### RegisterForPushNotifications Registers the device for push notifications and returns the push token. On iOS this triggers the system permission prompt, so call it at a point in the flow where asking makes sense to the user. Returns a String with the push token. ### UnregisterForPushNotifications Unregisters the device. It stops receiving notifications until `RegisterForPushNotifications` is called again. Returns a String with the token the device was unregistered with. ### GetPushToken Returns the current push token, or an empty string if the device is not registered yet. Returns a String. ### GetHwid Returns the [HWID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) of the device, the identifier Pushwoosh addresses it by. Use it to target a single device from the Pushwoosh Control Panel or the server API. Returns a String. ## User identity actions These actions connect a device to a person, so that one message reaches every device that person uses. ### SetUserId Associates the device with a user. Call it after login. | Parameter | Type | Description | | --------- | ------ | ------------------------------------------- | | `userId` | String | Your own identifier for the user. Required. | Returns a Boolean. ### GetUserId Returns the user ID currently associated with the device, or an empty string if none is set. Returns a String. ### SetEmails Registers the user’s email addresses with Pushwoosh, so they can be reached by email as well as by push. | Parameter | Type | Description | | --------- | ------ | ------------------------------------------------------ | | `emails` | String | One address, or several separated by commas. Required. | Returns a Boolean. ## Tag actions Tags are the device properties that segments are built from in the Pushwoosh Control Panel. ### SetTags Sets tags on the device. | Parameter | Type | Description | | ---------- | ------ | ---------------------------------------------------------------------------------- | | `tagsJson` | String | A JSON object, for example `{"Language":"en","Level":7,"Premium":true}`. Required. | Returns a Boolean. Quotes in Mendix expressions A string in a Mendix expression is written in single quotes, so the JSON inside it keeps its double quotes unescaped: `'{"Language":"en"}'`. Writing `""` or `\"` inside the expression produces a string the action cannot parse. ### GetTags Returns the tags currently set on the device, as a JSON string. Returns a String. ## Event actions Events are what triggered campaigns and in-app messages are started from. ### PostEvent Posts an event to Pushwoosh. | Parameter | Type | Description | | ---------------- | ------ | ------------------------------------------------------------ | | `eventName` | String | The event name as configured in the Control Panel. Required. | | `attributesJson` | String | Optional. A JSON object of event attributes. | Returns a Boolean. The underlying SDK call reports neither success nor failure, so the action returns true as soon as the call has been handed to the SDK. ## Local notification actions Local notifications are produced by the app itself and need no network, which makes them suitable for reminders. ### ScheduleLocalNotification Schedules a local notification on the device. | Parameter | Type | Description | | ---------------- | ------------ | ------------------------------------------------------------------------------ | | `message` | String | The notification text. Required. | | `afterSeconds` | Integer/Long | How long to wait before showing it, in seconds. Required, and zero or greater. | | `customDataJson` | String | Optional. A JSON object delivered back with the notification. | Returns a Boolean as soon as the call has been handed to the SDK. The SDK does not report back whether the notification was actually scheduled. ### ClearLocalNotifications Cancels local notifications that were scheduled but have not fired yet. Returns a Boolean as soon as the call has been handed to the SDK. ### ClearNotificationCenter Removes the app’s already-delivered notifications from the notification center. Returns a Boolean as soon as the call has been handed to the SDK. ## Privacy actions These actions back a consent toggle in your app, and switch all Pushwoosh messaging for the device on or off. ### IsCommunicationEnabled Tells whether the device is currently allowed to receive communication from Pushwoosh. Use it to render the state of a privacy toggle. Returns a Boolean. ### SetCommunicationEnabled Turns communication with Pushwoosh on or off for the device. Switching it off stops all messaging, which makes it the action to wire to a “do not contact me” toggle. | Parameter | Type | Description | | --------- | ------- | ---------------------------------------------------- | | `enabled` | Boolean | True to allow messaging, false to stop it. Required. | Returns a Boolean. ## Settings actions These actions change how the SDK behaves for this device. ### SetLanguage Overrides the language Pushwoosh uses to localize messages for the device. By default the device language is used. | Parameter | Type | Description | | ---------- | ------ | ----------------------------------------------------- | | `language` | String | A two-letter code such as `en`, `de`, `nl`. Required. | Returns a Boolean as soon as the call has been handed to the SDK. ### SetReverseProxy Routes all Pushwoosh traffic through your own endpoint. Call it before `Initialize`, otherwise the early requests still go straight to Pushwoosh. | Parameter | Type | Description | | ------------- | ------ | ---------------------------------------------- | | `url` | String | The proxy base URL. Required. | | `headersJson` | String | Optional. A JSON object of extra HTTP headers. | Returns a Boolean as soon as the call has been handed to the SDK. ### SetShowForegroundAlert Controls whether a notification that arrives while the app is open is shown as a system alert. The default is on, so turn it off to handle foreground notifications yourself through the **On push received** event of the widget. | Parameter | Type | Description | | ----------- | ------- | ---------------------------------------- | | `showAlert` | Boolean | True to show the system alert. Required. | Returns a Boolean as soon as the call has been handed to the SDK. iOS only The Android SDK does not expose this setting, and calling the action on Android raises an error. Call it on iOS only. ### GetShowForegroundAlert Tells whether a notification arriving while the app is open will be shown as a system alert. It is on by default. Returns a Boolean. iOS only The Android SDK does not expose this setting, and calling the action on Android raises an error. Call it on iOS only. ## Other channel actions These actions register the user for the messaging channels Pushwoosh supports beyond push. ### RegisterSmsNumber Registers the user’s phone number so Pushwoosh can reach them over SMS. | Parameter | Type | Description | | ------------- | ------ | -------------------------------------------------------------- | | `phoneNumber` | String | In international format, for example `+31201234567`. Required. | Returns a Boolean as soon as the call has been handed to the SDK. ### RegisterWhatsappNumber Registers the user’s phone number so Pushwoosh can reach them over WhatsApp. | Parameter | Type | Description | | ------------- | ------ | -------------------------------------------------------------- | | `phoneNumber` | String | In international format, for example `+31201234567`. Required. | Returns a Boolean as soon as the call has been handed to the SDK. --- # Outsystems **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/ How to implement push notifications into your Outsystems project --- ## Installing the Pushwoosh plugin Start by installing the Pushwoosh plugin from the [OutSystems Forge](https://www.outsystems.com/forge/component-overview/1556/pushwoosh-plugin). The fastest way to do this is to go to the OutSystems tab in your development environment and install it from there. ### Configuring Pushwoosh You’ll need to configure Pushwoosh for each of the platforms you will be supporting. Once your configuration is done, you’ll have an Application Code from Pushwoosh that will be used with your plugin. #### For iOS Learn how to [configure Pushwoosh for iOS](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration/). #### For Android Learn how to [configure Pushwoosh for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration/). ### Adding the Pushwoosh plugin to your application 1. In your application, use “Manage Dependencies…” to add a reference to the Pushwoosh plugin. 2. Add the “PushwooshNotifications” block to: * The “Layout” block if your application has no login * The “LayoutBlank” block if your application has login When adding the “PushwooshNotifications” block, avoid adding it inside placeholders, because some are only available at design time and, at runtime, Pushwoosh won’t work. 3. Set the following parameters in the PushwooshNotifications: * **PushwooshApplicationCode:** The Pushwoosh Code that you obtained after creating the application in Pushwoosh. * **FCMSenderId:** The identification number you obtained while configuring Android. You can leave this empty if your application is only available for iOS. * **NotificationReceivedEvent (Optional):** An event that is called when a notification is received while the application is in the foreground state. * **NotificationOpenedEvent (Optional):** An event that is called when a user clicks a notification on their device. If you’re using the Pushwoosh plugin in your Android app, do the following: 1. [Obtain the `google-services.json`](https://support.google.com/firebase/answer/7015592) from Firebase. The package name from the Google services must match the app identifier of the OutSystems application being built. 2. Create a zip file containing the `google-services.json` in the root. 3. Upload the “google-services.zip” file from the previous step to your OutSystems application and make sure to: * Set the “Name” as “google-services.zip” * Set “Deploy to Target Directory” as “google-services” ### Setting the Application Code at build time (recommended) Starting with Pushwoosh Plugin component version 4.7.0 (to update: install the newer version from the Forge, refresh the dependency in Manage Dependencies, and generate a new native build), the Application Code and the Device API token can be written into the native build itself. Add them as preferences to the Extensibility Configurations property of **your application**: ```json { "preferences": { "global": [ { "name": "PW_APPID", "value": "XXXXX-XXXXX" }, { "name": "PW_API_TOKEN", "value": "your Device API token" } ] } } ``` With this in place the SDK is configured before any JavaScript runs, so pushes delivered while the app is not running are handled correctly and push statistics are not lost. Preference names are case-sensitive — write them exactly as shown. `PW_API_TOKEN` is optional; the token can also be set at runtime with the `SetApiToken` client action. Passing the Application Code through the `PushwooshNotifications` block keeps working; when both are set, the runtime value wins. ### Registering the device For your device to receive push notifications, you’ll need to register it. If your users need to log in to use the application, go to the “Login” screen and edit the “Login” action. Add the code to register the device after the “DoLogin” action. It should look something like this: If your application does not have a login, you can add this code to the `OnInitializeOver` event in the `PushwooshNotifications` block. In this event handler, call the `RegisterDevice` action. If you’re generating an iOS mobile app using a developer certificate, you will need to add a preference to the Extensibility Configurations property of your OutSystems module: ```json { "preferences": { "global": [ { "name": "aps-environment", "value": "development" } ] } } ``` This preference will be added to your entitlements file (learn more about entitlements in the [Apple Developer documentation](https://developer.apple.com/library/content/documentation/Miscellaneous/Reference/EntitlementKeyReference/Chapters/AboutEntitlements.html)). If you’re generating an iOS mobile app using a production certificate, you won’t need to specify any preference, since the default value of the `aps-environment` preference in OutSystems is `production`. Your application is now ready to receive push notifications. [Configure and generate your mobile app](https://success.outsystems.com/documentation/11/deploying_apps/mobile_apps_packaging_and_delivery/) and install it on your device. At this point, you can use the Pushwoosh interface to send notifications to your app and make sure that everything is properly configured. Note that you will not see a notification if the app is running. Instead, the event `NotificationReceivedEvent` is raised immediately. ### Sending notifications To send notifications, you need a Pushwoosh [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/). To create one, go to **Settings → API Access** in the Pushwoosh Control Panel and click **Generate New Token**. ![API Access page in the Pushwoosh Control Panel with the Generate New Token button](https://docs.pushwoosh.com/outsystems8.webp) To send a notification to all devices, use the `SendPushNotificationToAll` action. This action takes the following parameters: * **PushwooshApplicationCode:** The Pushwoosh Code that you obtained after creating the application in Pushwoosh. * **ApiAccessToken:** The Pushwoosh API Access Token obtained earlier. * **Message:** The message text you wish to send. * **Badge:** A number that is displayed on the iOS application icon. * **Deeplink:** A deep link to send with the notification. Learn [how to define deep links](https://success.outsystems.com/documentation/how_to_guides/development/how_to_define_mobile_app_deep_links/). * **InboxDays** *(optional, since 4.7.0)*: How many days the message is kept in the user’s [Inbox](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/pushwoosh-outsystems-plugin-client-actions/#pushwoosh-inbox-client-actions), up to 30. Without it (and without InboxDate) the push is not saved to the Inbox. * **InboxDate** *(optional, since 4.7.0)*: The date until which the message is kept in the user’s Inbox. An alternative to InboxDays. * **InboxImage** *(optional, since 4.7.0)*: URL of a custom image shown next to the message in the Inbox. Two sibling server actions take the same parameters: `SendPushNotificationToToken` sends to a single device identified by its push token, and `SendPushNotificationToUserId` sends to all devices registered under a given User ID (set with the `SetUserId` client action). For instance, if you use this action to send the message “Hello from OutSystems” to an application called “PushTest,” the following would be shown in iOS: ### Additional information * If your app is running, the notification doesn’t show on the device. Instead, it is sent to the app, and the event `NotificationReceivedEvent` is raised. You’ll need to define what you want the app to do in that situation. * Push notifications may take a while to become active. If you don’t see the notifications coming through, wait a few minutes before further troubleshooting. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Huawei for Outsystems **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/huawei-for-outsystems/ How to set up Huawei for your Outsystems project --- ## Set up the Outsystems plugin Set up the Outsystems plugin for your project following the steps described in the guide: [Outsystems ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/) ## Get a Huawei configuration file 1. Go to [AppGallery Connect -> Projects List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject) and select a project to configure. 2. Press **agconnect-services.json** in the App information section to download a configuration file. 3. Create a zip file containing the **agconnect-services.json** in the root. Upload the “**agconnect-services.zip”** to your OutSystems application’s data resources and make sure to: * Set the “Name” as “agconnect-services.zip” * Set “Deploy to Target Directory” as “agconnect-services” ![OutSystems resource properties for agconnect-services.zip with Deploy to Target Directory and target directory agconnect-services](https://docs.pushwoosh.com/outsystems-huawei-for-outsystems-2.webp) ## Register the signing certificate fingerprint Huawei issues push tokens only to builds whose signing certificate is registered in AppGallery Connect. OutSystems builds are signed by the OutSystems build service (MABS), not by your own keystore — so take the fingerprint **from the built APK**: ```bash apksigner verify --print-certs your-app.apk ``` Copy the SHA-256 digest and add it in AppGallery Connect → Project settings → General information → **SHA-256 certificate fingerprint**. The MABS signing key is stable across rebuilds of the same application, so this is a one-time step. If the fingerprint is missing or wrong, token registration fails with Huawei error **6003** (“certificate fingerprint error”) and the device receives no pushes. ## Configure Huawei platform Refer to the [Huawei platform Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration/) for step-by-step guide on how to configure Huawei platform in Pushwoosh. ## Enable Huawei for your project Update the Pushwoosh Plugin component to version **4.7.0 or newer** — starting with it, the HMS Push library ships with the build. On earlier versions Huawei devices silently fell back to FCM (“Push Kit not integrated” in the device log), which does not work without Google services. To update: install the newer version of the Pushwoosh Plugin component from the Forge, refresh the dependency in your app (Manage Dependencies), and generate a new native build. Add the **EnableHuaweiPushNotifications** action prior to RegisterDevice. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Outsystems FAQ **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/outsystems-faq/ Frequently asked questions about Pushwoosh SDK integration with Outsystems --- ## Managing google-services files in projects with several possible package names In the plugin, you can add multiple `google-service.json` files to your project, which will be selected based on the package name set during the build. If you want to switch between multiple Firebase projects (each with its own `google-services.json` configuration file), follow these steps: 1. Create a ZIP archive. Place each `google-services.json` file into a `.zip` archive named `google-services.zip`. 2. When adding this `.zip` file to the project, set the action to Deploy to Target Directory and specify the target directory as `google-services/${package_name}`. For example, if the project package name is `com.pushwoosh.demo`, the target directory will be `google-services/com.pushwoosh.demo`. 3. During the project build, specify the corresponding package name. ## Resolving conflicts between multiple plugins depending on FirebaseMessagingService Android does not automatically resolve such conflicts, so it must be done manually. Since each plugin has its own logic, we cannot provide universal code that works for all possible plugins. Here’s how to address conflicts: 1. Write a router class in Java to handle the conflict. This class must be named `FirebaseMessagingRouterService`. Refer to the guide for an example: [Using Pushwoosh SDK with other FCM services](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq/#using-pushwoosh-sdk-with-other-fcm-services). 2. Name the Class File. The file containing this class must be named `FirebaseMessagingRouterService.java`. 3. Place this file into a `.zip` archive named `FirebaseMessagingRouterService.zip`. 4. Add this archive to the resources of your Outsystems project. 5. Set the action to Deploy to Target Directory, and specify the target directory as `FirebaseMessagingRouterService`. ## Each new notification replaces the previous one on Android By default, the Pushwoosh Android SDK works in single notification mode: every push is posted with the same notification id, so each new one replaces the previous. It is not related to the device vendor or to `collapse_key`. Starting with Pushwoosh Plugin component version 4.7.0, enable stacking with the ready-made **SetMultiNotificationMode** client action: 1. Update the Pushwoosh Plugin component from the Forge. 2. Refresh the dependency to the PushwooshPlugin module (Manage Dependencies). 3. Add `SetMultiNotificationMode` to a flow that runs after initialization — for example, the handler of the `OnInitializeOver` event, right after `RegisterDevice`. The action takes no parameters, and one successful call is stored by the SDK and survives restarts. Each push then appears as a separate notification. Android groups them into one expandable stack with a counter — that is standard system grouping. Note: pushes sent with an `android_tag` value still replace each other regardless of the mode. --- # Pushwoosh Outsystems plugin client actions **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/pushwoosh-outsystems-plugin-client-actions/ A list of client actions available --- The Plugin provides a set of client actions to facilitate push notification management. These actions allow you to register and unregister devices, set and get tags, manage inbox messages, and more. ## Pushwoosh plugin client actions ### CheckPushwooshPlugin Validates if the Pushwoosh plugin is currently available. #### Output parameters | IsAvailable | Returns true if the plugin is available to use, false otherwise | | ----------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | #### Usage example ![CheckPushwooshPlugin action](https://docs.pushwoosh.com/outsystems-pushwoosh-outsystems-plugin-client-actions-1.webp) ### RegisterDevice Registers a device for push notifications and retrieves a Push Token. #### Input parameters | ApplicationCode | Text, the Pushwoosh application code obtained from your account settings. | | --------------- | ------------------------------------------------------------------------- | #### Output parameters | RegisterToken | Token received from Google or Apple for the device | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Success | Returns true if everything goes as expected, false if there is an error. | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### UnregisterDevice Unregisters a device from receiving push notifications. #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### GetDeviceID Retrieves the device ID, which should be a unique identifier. #### Output parameters | DeviceHwid | The unique identifier of the device. | | ---------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when there was an error. | ### GetDeviceType Gets the Device Type (Android, iOS, or Windows). #### Output parameters | DeviceTypeId | The identifier of this device type. | | ------------ | ----------------------------------- | ### GetTags Fetches tags data from Pushwoosh. #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | | Tags | Returns Key, Value Record List containing all tags assigned to this device. | #### Usage example 1. Invoke the **GetTags** action to fetch the tags data from Pushwoosh. 2. The returned tags are accessible via the **GetTags.Tags** list. 3. Use the **LogMessage** action to log each tag’s key and value. ### PostEvent Sends a message to Pushwoosh when a particular event in your application happens. #### Input parameters | EventName | Text, name of the event | | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | EventAttributes | Key, Value Record List, used to send additional event data to Pushwoosh (i.e. for the “UserLogin” action you might want to send login data, such as - `{"username":"John"}`) | Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | #### Usage example **1. Assign** Set up key-value pairs to define the data for the event. For instance, KeyValue1.AttributeName as “test1” and KeyValue1.Value as “test1value”. Similarly, define KeyValue2 with the appropriate attribute name and value. 2. Create lists of attributes to be associated with the event. 3. Finally, configure the **PostEvent** action. ### SetBadgeNumber Sets the application icon badge number. #### Input parameters | BadgeNumber | Integer, Icon badge number. | | ----------- | --------------------------- | #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### SetLanguage Sets custom application language instead of the default system language. #### Input parameters | Language | Text, Custom application language in 2-letter ISO format (e.g., “en”, “es”, “fr”) | | -------- | --------------------------------------------------------------------------------- | ### SetTags Sends tags and their values to Pushwoosh. #### Input parameters | Tags | TagName, TagValue Record List containing all tags you want to send. TagName type should always be Text, while TagValue can be Text, Integer, Boolean, Date, etc. | | ---- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | #### Usage example 1. Assign tag values. In the example below, there are two tags: * Tag1 with a name testTag and its value set to the current date. * Tag2 with a name testTag2 and its value set to True. 2. Then, append tags to a list. In this example: * ListAppend3 adds Tag1 to the LocalTag list. * ListAppend4 adds Tag2 to the LocalTag list. 3. After populating the list with the necessary tags, use the **SetTags** client action to send this list to Pushwoosh. ### SetUserId Assigns a custom User ID to this device. #### Input parameters | UserID | Text, custom User ID. | | ------ | --------------------- | #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### SetApiToken Sets the Pushwoosh Device API token used by the SDK. Call it before `RegisterDevice`. The token can also be set at build time with the `PW_API_TOKEN` preference — see the [integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/). #### Input parameters | Token | Text, the Device API token from the Pushwoosh Control Panel (API Access section). | | ----- | --------------------------------------------------------------------------------- | ### EnableHuaweiPushNotifications Switches the push transport to Huawei Push Kit (HMS). Call it before `RegisterDevice` on Huawei devices. Requires the Huawei configuration — see [Huawei for Outsystems](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/outsystems/huawei-for-outsystems/). The action has no output parameters and does not report success or failure. To verify which transport the device actually registered with, use `GetDiagnostics`. ### SetMultiNotificationMode *Available since Pushwoosh Plugin component version 4.7.0.* **Android only** — makes every push appear as a separate notification instead of replacing the previous one. Call it once after initialization — for example, in the handler of the `OnInitializeOver` event, after `RegisterDevice`. The setting is stored by the SDK and survives app restarts. Android groups the notifications into one expandable stack — that is standard system behavior. There is no iOS implementation (iOS never replaces notifications) — guard the call with `GetDeviceType` rather than putting it into a flow that also runs on iOS. ### GetDiagnostics *Available since Pushwoosh Plugin component version 4.7.0.* Returns the integration state as a JSON string: the effective and built-in Application Code and where it came from, API token presence, HWID, push token, notification permission state, and the plugin and native SDK versions. On Android it additionally includes the User ID and the list of services registered for FCM/HMS messaging events (these two fields are not present on iOS). Attach its output to support tickets instead of collecting device logs. #### Output parameters | DiagnosticsJSON | Text, the integration state as a JSON string. | | --------------- | ----------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. | Example output (Android): ```json { "platform": "android", "packageName": "com.example.app", "appIdEffective": "XXXXX-XXXXX", "appIdManifest": "XXXXX-XXXXX", "appIdSource": "manifest", "apiTokenInManifest": false, "hwid": "d3ba13b6-…", "pushToken": "eAvqvOE0…", "userId": "d3ba13b6-…", "notificationsEnabled": true, "messagingServices": [ { "action": "com.google.firebase.MESSAGING_EVENT", "class": "com.pushwoosh.firebase.PushFcmIntentService", "priority": 0 } ], "pluginVersion": "8.3.72-OS.1", "nativeSdkVersion": "6.11.1" } ``` ## Pushwoosh Inbox Client Actions The process of working with Inbox is the following: 1. Load a list of InboxMessage objects. 2. Use these objects to render the inbox view. 3. Perform the actions listed below, or delete these objects using the actions described below. | code | Text | Unique ID of an inbox notification. | | ----------------- | ---------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | title | Text | Notification title. | | message | Text | Notification message. | | imageUrl | Text | URL of a custom image attached to a notification. | | sendDate | DateTime | Represents the time when a notification was sent. | | type | Integer | Type of notification.Possible values:- 0 - regular notification - 1 - rich media notification - 2 - URL notification - 3 - deep link notification | | bannerUrl | Text | Contains URL of an Android banner. | | customData | Key, Value Record List | Contains a list of parameters passed as custom data. | | isRead | Boolean | True if inbox notification is read. | | isActionPerformed | Boolean | * **True** if notification action is performed. * For regular notifications, **true** if a notification is read. * For Rich Media, **true** if rich media was presented. * For URL and deep links, **true** if URL/deep link was opened. | ### LoadMessages Fetches inbox notifications from Pushwoosh. #### Output parameters | InboxMessages | InboxMessage List, list of all inbox messages. | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Success | Returns **true** if everything goes as expected, **false** if there is an error. | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### ReadMessage Marks the inbox message as read. #### Input parameters | MessageCode | Text, code of an InboxMessage (received as a parameter of an InboxMessage in LoadMessages) | | ----------- | ------------------------------------------------------------------------------------------ | #### Output parameters | Success | True if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### PerformAction Performs action for specified inbox messages (e.g., open URL in a browser). #### Input parameters | MessageCode | Text, code of an InboxMessage (received as a parameter of an InboxMessage in LoadMessages) | | ----------- | ------------------------------------------------------------------------------------------ | #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### DeleteMessage Removes a message from inbox. #### Input parameters | MessageCode | Text, code of an InboxMessage (received as a parameter of an InboxMessage in LoadMessages) | | ----------- | ------------------------------------------------------------------------------------------ | #### Output parameters | Success | Returns true if everything goes as expected, false if there is an error. | | ------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### TotalMessagesCount Returns the total number of inbox messages. #### Output parameters | MessagesCount | Integer, total number of inbox messages. | | ------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Success | Returns true if everything goes as expected, false if there is an error. | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### UnreadMessagesCount Returns the number of unread inbox messages. #### Output parameters | UnreadMessagesCount | Integer, number of unread inbox messages. | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Success | Returns true if everything goes as expected, false if there is an error. | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | ### MessagesWithNoActionPerformedCount Returns the number of inbox messages with no action performed. #### Output parameters | MessagesWithNoActionPerformedCount | Integer, number of inbox messages with no action performed. | | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Success | Returns true if everything goes as expected, false if there is an error. | | Error | An error containing the error code and error message with details about any error that might have occurred. Only set when the Success output parameter is false. | --- # React Native Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/ Explore the official Pushwoosh React Native SDK documentation. Find integration guides, API reference, configuration options, examples & support to add push notifications to your React Native app --- The Pushwoosh React Native SDK adds push notifications to a React Native app. This page lists the available integration methods and guides to get started. ## Setting up React Native SDK Choose the integration method that best suits your needs to get started with the Pushwoosh React Native SDK: * [AI-Assisted Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/ai-integration/) - use AI coding assistants to integrate and configure the Pushwoosh React Native plugin Or doing by yourself: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/quick-start/) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrating-react-native-plugin/) - a step-by-step approach to setting up the React Native SDK from scratch * [Integrating the React Native Huawei plugin](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrate-huawei-react-native/) - learn how to enable push notification support specifically for Huawei devices using the Huawei plugin --- # AI-Assisted Integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/ai-integration/ Integrate Pushwoosh React Native plugin using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP --- Use AI coding assistants to integrate and configure the Pushwoosh React Native plugin in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date plugin documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh React Native plugin docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh React Native plugin library ID on Context7: **`/pushwoosh/pushwoosh-react-native-plugin`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the plugin. Below are ready-to-use prompts for common tasks. ### Basic plugin integration ```text Integrate Pushwoosh React Native plugin into my React Native project. Requirements: - Install pushwoosh-react-native-plugin via npm - Initialize Pushwoosh with my App ID in App component - Register for push notifications and handle pushReceived and pushOpened events Use Context7 MCP to fetch Pushwoosh React Native plugin documentation. ``` ### Push notification handling ```text Show me how to handle push notification callbacks (receive, open) with Pushwoosh React Native plugin. I want to process push payload data and navigate to specific screens based on custom data. Use Context7 MCP to fetch Pushwoosh React Native plugin documentation for push event handling. ``` ### User segmentation with tags ```text Show me how to use Pushwoosh tags for user segmentation in a React Native app. I need to set tags, get tags, and set user ID for cross-device tracking. Use Context7 MCP to fetch Pushwoosh React Native plugin documentation for setTags and getTags. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh React Native plugin documentation”** at the end of your prompt tells the AI assistant to pull the latest plugin docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh React Native plugin, it receives: * **Installation instructions** — npm/yarn setup and platform-specific configuration * **API reference** — public methods, event listeners, and callback signatures * **Code examples** — JavaScript and TypeScript snippets for common use cases * **Event documentation** — pushReceived, pushOpened, and deep link event handlers * **Configuration details** — initialization parameters, iOS and Android setup steps --- # Customizing Pushwoosh React Native plugin **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/customizing-pushwoosh-react-native-plugin/ Customize the Pushwoosh React Native plugin to avoid conflicts when working with other FCM services --- ## Using Pushwoosh plugin with other FCM services If your app uses other plugins that rely on **firebase-messaging**, it might result in conflicts, or in one of the plugins not working as expected. To resolve this conflict, you can add a custom FirebaseMessagingService class that will route push notifications between the plugins. Pushwoosh React Native plugin contains a template of this class which you can use as is to force all notifications go through Pushwoosh handlers, or modify it to handle notifications from other providers as well. To enable the template, uncomment the following lines in the plugin’s `AndroidManifest.xml`: ```plaintext ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Integrate Huawei React Native plugin **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrate-huawei-react-native/ How to set up integration with React Native Huawei plugin --- ## Integrate the React Native plugin Refer to the React Native integration guide. [React Native integration guide ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrating-react-native-plugin/) ## Configure Huawei via AppGallery Connect 1. Go to [AppGallery Connect -> Projects List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject) and select a project to configure. 2. Press **agconnect-services.json** in the App information section to download a configuration file. 3. Put the downloaded file into your Android Studio project (YOUR_PROJECT/app/agconnect-services.json). ## Generate Signing Certificate Fingerprint 1. In Android Studio, go to View > Tool Windows > Gradle. 2. In the window opened, go to app > Tasks > android > signingReport. 3. Copy SHA-256 from your release keystore. You can also add the SHA-256 from your debug keystore for testing purposes. 4. Select your project in [AppGallery Connect Project List](https://developer.huawei.com/consumer/en/service/josp/agc/index.html#/myProject). 5. Paste the SHA-256 into the **SHA-256 certificate fingerprint** field. Please be aware that fingerprint processing may take up to 10 minutes. ## Configure Huawei in Pushwoosh Follow the guide to configure the Huawei platform for your Pushwoosh project: [Huawei Configuration ](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration/) ## Add dependencies Add dependencies to your Android project: * To `android/build.gradle` ```plaintext buildscript { repositories { google() maven { url 'https://developer.huawei.com/repo/' } }​ dependencies { ... classpath 'com.huawei.agconnect:agcp:1.4.1.300' } }​ allprojects { repositories { google() maven { url 'https://developer.huawei.com/repo/' } } } ``` * To `android/app/build.gradle` ```plaintext apply plugin: "com.huawei.agconnect"​ ...​ dependencies { ... implementation 'com.huawei.hms:push:6.1.0.300' } ``` ## Enable push notifications Add **Pushwoosh.enableHuaweiPushNotification();** before calling **Pushwoosh.register()** in your React Native project. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # React Native SDK Basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrating-react-native-plugin/ Learn how to integrate the Pushwoosh React Native SDK, configure users, track events, set tags, and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh React Native SDK into your application. ## Prerequisites To integrate the Pushwoosh React Native SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Add Pushwoosh React Native SDK Dependency Add the Pushwoosh React Native SDK dependency to your project: ```bash npm install pushwoosh-react-native-plugin --save ``` ### 2. React Native SDK Initialization In the root component of your `index.js` file: * Import the `pushwoosh-react-native-plugin`. * Initialize the Pushwoosh SDK. * Call `register()` in your initialization logic to register for push notifications. index.js ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; AppRegistry.registerComponent(appName, () => App); Pushwoosh.init({ "pw_appid" : "__YOUR_APP_ID__" }); Pushwoosh.register(); ``` Where: * `__YOUR_APP_ID__` is the application code from the Pushwoosh Control Panel. ### 3. iOS Native Setup #### 3.1 Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. #### 3.2 Info.plist In your `Runner/Info.plist` set the `__PUSHWOOSH_DEVICE_API_TOKEN__` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): info.plist ```swift Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` #### 3.3 Message delivery tracking You must add a Notification Service Extension target to your project. This is essential for accurate delivery tracking and features like Rich Media on iOS. Follow the [native guide’s steps](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target and the necessary Pushwoosh code within it. #### 3.4 Installing dependencies for the iOS React Native project To install dependencies for the iOS React Native project navigate to the `ios` folder in the terminal and run: ```bash pod install --repo-update ``` ### 4. Android Native Setup #### 4.1 Install dependencies Ensure that the required dependencies and plugins are added to your Gradle scripts: Add the Google Services Gradle plugin to your project-level `build.gradle` dependencies: android/build.gradle ```groovy buildscript { dependencies { classpath 'com.google.gms:google-services:4.3.15' } } ``` Apply the plugin in your app-level `build.gradle` file: app/build.gradle ```groovy apply plugin: 'com.google.gms.google-services' ``` #### 4.2 Add Firebase configuration file Place the `google-services.json` file into `android/app` folder in your project directory. #### 4.3 Add Pushwoosh metadata In your `main/AndroidManifest.xml` add [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) inside the `` tag: AndroidManifest.xml ```xml ``` > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 5. Run the Project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notification event listeners In the Pushwoosh SDK there are two event listeners, designed for handling push notifications: * `pushReceived` event is triggered, when a push notification is received * `pushOpened` event is triggered, when a user opens a notification You should set up these event listeners right after initialization of the SDK at the application start up: ```javascript import {DeviceEventEmitter} from 'react-native' import Pushwoosh from 'pushwoosh-react-native-plugin'; class PushwooshNotificationHandler { setupPushListeners() { DeviceEventEmitter.addListener('pushReceived', (e) => { console.warn("Push received: " + JSON.stringify(e)); }); DeviceEventEmitter.addListener('pushOpened', (e) => { console.warn("Push accepted: " + JSON.stringify(e)); }); } } ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; class Registration { afterUserLogin(user: User) { // Set user ID Pushwoosh.setUserId(user.getId()); // Set user email Pushwoosh.setEmails(user.getEmailList()); // Set user ID and email in a single call Pushwoosh.setUserEmails(user.getId(), user.getEmailList()); // Set user SMS number Pushwoosh.registerSMSNumber(user.getPhoneNumber()); // Set user WhatsApp number Pushwoosh.registerWhatsappNumber(user.getWhatsAppNumber()); // Setting additional user information as tags for Pushwoosh Pushwoosh.setTags({ "age": user.getAge(), "name": user.getName(), "last_login": user.getLastLoginDate() }); } } ``` Caution On iOS, the `fail` callback of `setEmails`/`setUserEmails` is not invoked — `success` fires regardless of the actual result, so check the device logs to confirm the call succeeded. On Android, both `success` and `fail` fire correctly. ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; class UpdateUser { afterUserUpdateProfile(user: User) { // Set list of favorite categories Pushwoosh.setTags({ "favorite_categories": user.getFavoriteCategoriesList() }); // Set payment information Pushwoosh.setTags({ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() }); } } ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; class Registration { // Track login event afterUserLogin(user: User) { Pushwoosh.postEvent("login", { "name": user.getName(), "last_login": user.getLastLoginDate() }); } // Track purchase event afterUserPurchase(product: Product) { Pushwoosh.postEvent("purchase", { "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() }); } } ``` ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # React Native SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/quick-start/ Get started with the Pushwoosh React Native SDK using this quick and simple example. --- Get started with the Pushwoosh React Native SDK using this quick and simple example ## Prerequisites To integrate the Pushwoosh React Native SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project ```bash # Clone the SDK repository and navigate to the example project git clone https://github.com/Pushwoosh/pushwoosh-react-native-plugin.git # Navigate to example directory cd pushwoosh-react-native-plugin/example/demoapp/ ``` ## Configure the project Install the dependencies: ```bash npm install ``` Open **Settings.js** and update the `Pushwoosh.init` call: ```javascript Pushwoosh.init({ "pw_appid" : "__YOUR_APP_ID__" }); ``` Replace `__YOUR_APP_ID__` with your **Pushwoosh App Code**. ### iOS ###### 1. Install dependencies Navigate to the ios directory and install the dependencies: ```bash cd ios && pod install --repo-update ``` ###### 2. Set the Bundle Identifier Open the **ios/Runner.xcworkspace** file in Xcode. Set the **Bundle Identifier** for each target: * `Runner`: your app’s package name. (eg. com.example.pushwoosh) * `NotificationService`: your app’s package name with `.NotificationServiceExtension` (eg. com.example.pushwoosh.NotificationServiceExtension) ###### 3. Set the API Token In your `ios/Runner/Info.plist` set the `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### Android ###### 1. Setup google-services.json Copy your `google-services.json` file to the **android/app** directory ###### 2. Set the package name Open **android/app/build.gradle** and set the **applicationId** to your app’s package name. (eg. `com.example.pushwoosh`) ###### 3. Set the API Token Open your **android/app/src/main/AndroidManifest.xml** file and add the following metadata inside the `` tag: AndroidManifest.xml ```xml ``` Replace `__YOUR_PUSHWOOSH_DEVICE_API_TOKEN__` with your [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). ## Run the project 1. Build and run the project. 2. The app will have two tabs: “Actions” and “Settings”. Navigate to the **Settings** tab. 3. Tap the **Register for Push Notifications** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Setting up badges for React Native **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/setting-up-badges-for-react-native/ How to implement Badges for your React Native project --- Since the React Native plugin does not contain the PWNotificationExtensionManager header, you have to perform several additional steps to implement Badges for your React Native project. Note To implement badges for iOS Native app, please refer to the [Setting up Badges](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges/) guide. #### 1. Add Notification Service Extension (File -> New -> Target…) #### 2. Select the “Notification Service Extension” #### 3. Update the Notification Service Extension code as follows: ```plaintext #import "NotificationService.h" #import @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end ``` Note Replace the name of extension used in the line `#import "NotificationService.h"` with the name of your extension. #### 4. Link dependencies to a target React Native does not automatically link all required dependencies to a target that was created manually, so you should update the target’s **Build Phases** and provide the necessary info: Just click on the **+** sign under the **Compile Sources** and **Link Binary With Libraries** and search for the required libs/frameworks. #### 5. Add **App Groups** capability to both targets in Xcode and set the same group **for both app and extension**: #### 6. Add the **PW_APP_GROUPS_NAME** key to info.plists of **both** targets. The value is the name of your app group: --- # Setting up Message Inbox for React Native **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/setting-up-message-inbox-for-react-native/ Show the Message Inbox screen in a React Native app, style it, and manage inbox messages using the Pushwoosh React Native plugin. --- The Pushwoosh React Native plugin ships Message Inbox (the “bell icon” App Inbox) as part of the same `pushwoosh-react-native-plugin` package used for push notifications. No extra install step is needed. ## Prerequisites * The [base Pushwoosh React Native SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/integration/integrating-react-native-plugin/) already integrated and sending pushes. ## Show the inbox ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; Pushwoosh.presentInboxUI(); ``` `presentInboxUI()` presents a native screen (a view controller on iOS, an Activity on Android) outside the React view tree, with the SDK’s default appearance. ## Customize the style Pass a style object to `presentInboxUI()`. Color values must go through React Native’s `processColor()`. Every key is optional: | Key | Customizes | | ---------------------- | -------------------------------------------------- | | `dateFormat` | Date formatting | | `defaultImageIcon` | The default icon next to a message with no image | | `unreadImage` | The unread-messages mark (iOS only) | | `listErrorImage` | Shown when loading the list fails | | `listEmptyImage` | Shown when the list has no messages | | `listErrorMessage` | Error text (not localized) | | `listEmptyMessage` | Empty-list text (not localized) | | `defaultTextColor` | Default text color (iOS only) | | `accentColor` | Accent color | | `backgroundColor` | Background color | | `highlightColor` | Selection color | | `titleColor` | Message title color | | `readTitleColor` | Title color for read messages (Android only) | | `descriptionColor` | Message description color | | `readDescriptionColor` | Description color for read messages (Android only) | | `dateColor` | Message date color | | `readDateColor` | Date color for read messages (Android only) | | `dividerColor` | Separator color | | `barBackgroundColor` | The title bar’s background color | | `barAccentColor` | The title bar’s back-button color | | `barTextColor` | The title bar’s text color | ```javascript import Pushwoosh from 'pushwoosh-react-native-plugin'; import { processColor } from 'react-native'; Pushwoosh.presentInboxUI({ dateFormat: "dd.MM.yyyy", accentColor: processColor('#3498db'), backgroundColor: processColor('#ffffff'), titleColor: processColor('#333333'), descriptionColor: processColor('#666666'), listEmptyMessage: "No messages yet" }); ``` ## Manage messages programmatically Load the message list, read counts, and act on individual messages without opening the built-in screen. This is useful for a custom inbox UI or an unread badge: ```javascript Pushwoosh.loadMessages( (messages) => { messages.forEach((msg) => { console.log(msg.title + ": " + msg.message); }); }, (error) => console.error("Failed to load: " + error) ); Pushwoosh.unreadMessagesCount((count) => { console.log("Unread messages: " + count); }); Pushwoosh.messagesCount((count) => { /* total messages */ }); Pushwoosh.messagesWithNoActionPerformedCount((count) => { /* not yet acted on */ }); Pushwoosh.readMessage(messageId); Pushwoosh.deleteMessage(messageId); Pushwoosh.performAction(messageId); // runs the message's default action, e.g. opening a URL ``` --- # Unity SDK overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/ Explore the official Pushwoosh Unity SDK documentation for integration guides, configuration options, and examples to add push notifications to your Unity app. --- ## Setting up Unity SDK Choose the integration method that best suits your needs to get started with the Pushwoosh Unity SDK: * [AI-assisted integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/ai-integration/) — use AI coding assistants to integrate and configure the Pushwoosh Unity plugin * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/quick-start/) — set up basic push notifications in minutes with a quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/basic-integration-guide/) — a step-by-step approach to setting up the Unity SDK from scratch ## SDK architecture The Pushwoosh Unity SDK uses a modular UPM (Unity Package Manager) architecture with separate packages for each platform: | Package | Description | | ----------------------------- | --------------------------------- | | `com.pushwoosh.unity.core` | Cross-platform API **(required)** | | `com.pushwoosh.unity.android` | Android platform support | | `com.pushwoosh.unity.ios` | iOS platform support | | `com.pushwoosh.unity.windows` | Windows UWP/WSA support | Install only the core package and the platform packages you need. --- # AI-assisted integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/ai-integration/ Integrate Pushwoosh Unity SDK using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP. --- Use AI coding assistants to integrate and configure the Pushwoosh Unity SDK in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date plugin documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh Unity SDK docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh Unity SDK library ID on Context7: **`/Pushwoosh/pushwoosh-unity`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the SDK. Below are ready-to-use prompts for common tasks. ### Basic SDK integration ```text Integrate Pushwoosh Unity SDK into my Unity project. Requirements: - Install com.pushwoosh.unity.core and com.pushwoosh.unity.android via UPM - Initialize Pushwoosh with my App ID - Register for push notifications and handle OnPushNotificationsReceived and OnPushNotificationsOpened events Use Context7 MCP to fetch Pushwoosh Unity SDK documentation. ``` ### Tags and user segmentation ```text Show me how to use Pushwoosh tags in a Unity app for user segmentation. I need to set string, int, and list tags, get tags, and set user ID. Use Context7 MCP to fetch Pushwoosh Unity SDK documentation for SetStringTag and GetTags. ``` ### Events and purchase tracking ```text Integrate Pushwoosh event tracking in my Unity app. Show me how to: - Post custom events with attributes - Track in-app purchases with SendPurchase Use Context7 MCP to fetch Pushwoosh Unity SDK documentation for PostEvent. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh Unity SDK documentation”** at the end of your prompt tells the AI assistant to pull the latest SDK docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh Unity SDK, it receives: * **Installation instructions** — UPM setup via npm registry and Git URL * **API reference** — public classes, methods, and event definitions * **Code examples** — C# snippets for common use cases * **Platform setup** — iOS and Android configuration steps * **Configuration details** — initialization parameters, EDM4U setup --- # Unity SDK basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/basic-integration-guide/ Learn how to integrate the Pushwoosh Unity SDK, configure users, track events, set tags, and handle push notifications. --- This guide walks you through integrating the Pushwoosh Unity SDK into your application. ## Prerequisites Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * Unity 2021.3 or later. * **For iOS:** * An iOS platform configured to send push notifications. We recommend using [Token-Based Authentication](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * Set the Gateway to `Sandbox` to send pushes to a simulator. * To get the native iOS SDK with Swift Package Manager: Unity 2021.3.15 or later, **Target minimum iOS Version** set to 15.0 or later, and access to github.com when Xcode builds the project. With Unity 2021.3.0–2021.3.14 or a lower iOS target, the SDK is added with CocoaPods (see [Choose how the iOS SDK is added](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/basic-integration-guide/#3-choose-how-the-ios-sdk-is-added)). * **For Android:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration). * The `project number` (also known as Sender ID), `google-services.json` file, and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel. ## Integration steps ### 1. Add Pushwoosh Unity SDK * UPM via Scoped Registry (recommended) Add the following to your `Packages/manifest.json`: Packages/manifest.json ```json { "dependencies": { "com.pushwoosh.unity.core": "6.3.0", "com.pushwoosh.unity.android": "6.3.0", "com.pushwoosh.unity.ios": "6.3.0" }, "scopedRegistries": [ { "name": "npmjs", "url": "https://registry.npmjs.org", "scopes": ["com.pushwoosh"] } ] } ``` Add only the platform packages you need. For example, omit `com.pushwoosh.unity.android` if you only target iOS. * UPM via Git URL In Unity, go to **Window > Package Manager > + > Add package from git URL** and add the following URLs one by one: ```plaintext https://github.com/Pushwoosh/pushwoosh-unity.git?path=com.pushwoosh.unity.core https://github.com/Pushwoosh/pushwoosh-unity.git?path=com.pushwoosh.unity.android https://github.com/Pushwoosh/pushwoosh-unity.git?path=com.pushwoosh.unity.ios ``` * .unitypackage Download `Pushwoosh.unitypackage` from [GitHub Releases](https://github.com/Pushwoosh/pushwoosh-unity/releases) and import via **Assets > Import Package > Custom Package**. ### 2. Install External Dependency Manager The Android part of the SDK requires [External Dependency Manager for Unity (EDM4U)](https://github.com/googlesamples/unity-jar-resolver) to resolve native Android dependencies. Skip this step if you only target iOS. Add the following scoped registry to your `Packages/manifest.json`: ```json { "scopedRegistries": [ { "name": "package.openupm.com", "url": "https://package.openupm.com", "scopes": ["com.google.external-dependency-manager"] } ] } ``` Then add the package to your dependencies: ```json "com.google.external-dependency-manager": "1.2.183" ``` ### 3. Choose how the iOS SDK is added By default, the iOS export gets the native SDK from the Swift package [Pushwoosh-XCFramework](https://github.com/Pushwoosh/Pushwoosh-XCFramework) with Swift Package Manager. You don’t need CocoaPods or EDM4U for iOS, and you don’t run `pod install`. To change the source of the iOS SDK or add optional modules, open **Window > Pushwoosh SDK Setup** in the Unity Editor: * In **iOS SDK from**, select **Swift Package Manager** or **CocoaPods**. * With Swift Package Manager, under **Modules (Swift Package Manager only)**, select the optional modules your app uses: `PushwooshLiveActivities`, `PushwooshInboxKit`, `PushwooshInApp`, `PushwooshVoIP`, `PushwooshForegroundPush`, `PushwooshKeychain`, `PushwooshGRPC`, `PushwooshGeozones`. The modules are linked to the app target. To use a module in your own app extension, add it to the extension target in Xcode. With CocoaPods, the export goes without these modules. The export falls back to CocoaPods, and the Unity console states the reason, when any of these applies: * **CocoaPods** is selected in **Window > Pushwoosh SDK Setup**. * The project already has a `PushwooshXCFramework` pod, in a `*Dependencies.xml` file read by EDM4U or in the Podfile. To switch to Swift Package Manager, remove the pod. For Geozones, remove the `PushwooshXCFramework/Geozones` pod and select `PushwooshGeozones` in **Window > Pushwoosh SDK Setup**. * The Unity version is earlier than 2021.3.15. * **Target minimum iOS Version** is below 15.0. With CocoaPods, EDM4U isn’t required. The SDK runs `pod install` after the export and generates the Podfile itself, unless EDM4U is installed and writes the Podfile instead. If `pod install` fails, the Unity console shows the error. Run `pod install` in the exported Xcode project folder. If your build environment can’t reach github.com, Xcode can’t resolve the Swift package. Select **CocoaPods** in **Window > Pushwoosh SDK Setup** for such builds. The Swift Package Manager export keeps `Unity-iPhone.xcworkspace`, so build scripts and CI that build the workspace work without changes. Updating from a CocoaPods setup When you update to 6.3.0 or later, a project that got the iOS SDK through CocoaPods or EDM4U switches to Swift Package Manager, unless one of the conditions above keeps it on CocoaPods. * To keep CocoaPods, select it in **Window > Pushwoosh SDK Setup**. * If you export with Swift Package Manager into the folder of a previous CocoaPods export, the Pushwoosh pod is removed from the Podfile, but the Xcode project still references Pods and builds only from `Unity-iPhone.xcworkspace`. Build the workspace, or export into a new folder to get a project without CocoaPods. ### 4. Initialize the SDK Create a `PushNotificator.cs` script and attach it to any GameObject in the scene: PushNotificator.cs ```csharp using UnityEngine; using System.Collections.Generic; public class PushNotificator : MonoBehaviour { void Start() { Pushwoosh.ApplicationCode = "XXXXX-XXXXX"; Pushwoosh.FcmProjectNumber = "XXXXXXXXXXXX"; Pushwoosh.Instance.OnRegisteredForPushNotifications += (token) => { Debug.Log("Push token: " + token); }; Pushwoosh.Instance.OnFailedToRegisteredForPushNotifications += (error) => { Debug.Log("Registration failed: " + error); }; Pushwoosh.Instance.RegisterForPushNotifications(); } } ``` Replace: * `XXXXX-XXXXX` with your Pushwoosh Application Code. * `XXXXXXXXXXXX` with your Firebase project number (Android only). ### 5. iOS native setup #### 5.1 Capabilities After building the iOS project from Unity, open the generated Xcode project and add the following capabilities in **Signing & Capabilities**: * **Push Notifications** * **Background Modes** with **Remote notifications** checked For Time Sensitive Notifications (iOS 15+), also add the **Time Sensitive Notifications** capability. #### 5.2 Info.plist Add the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to your `Info.plist`: Info.plist ```xml Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` #### 5.3 Message delivery tracking Add a Notification Service Extension target to your Xcode project. This is required for accurate delivery tracking and Rich Media on iOS. Follow the [native guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#4-message-delivery-tracking) to add the extension target. ### 6. Android native setup #### 6.1 Add Firebase configuration file Place the `google-services.json` file into your Unity project’s **Assets** directory. #### 6.2 Add Pushwoosh metadata Add the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) to your `Assets/Plugins/Android/AndroidManifest.xml` inside the `` tag: AndroidManifest.xml ```xml ``` Caution Be sure to give the token access to the correct app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 7. Run the project 1. Build and run the project on your target platform. 2. Grant permission for push notifications when prompted. 3. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). ## Extended integration At this stage, you can send and receive push notifications. The sections below cover the core SDK functionality. ### Push notification event listeners The SDK provides two event listeners for handling push notifications: * `OnPushNotificationsReceived` — triggered when a push notification arrives * `OnPushNotificationsOpened` — triggered when a user taps a notification Set up these listeners during SDK initialization: PushNotificator.cs ```csharp void Start() { Pushwoosh.ApplicationCode = "XXXXX-XXXXX"; Pushwoosh.FcmProjectNumber = "XXXXXXXXXXXX"; Pushwoosh.Instance.OnPushNotificationsReceived += (payload) => { Debug.Log("Push received: " + payload); }; Pushwoosh.Instance.OnPushNotificationsOpened += (payload) => { Debug.Log("Push opened: " + payload); }; Pushwoosh.Instance.RegisterForPushNotifications(); } ``` ### User configuration Personalize push notifications by identifying users and setting their properties: ```csharp // Set user ID for cross-device tracking Pushwoosh.Instance.SetUserId("user-123"); // Set user email Pushwoosh.Instance.SetEmail("user@example.com"); // Set user with both ID and email Pushwoosh.Instance.SetUser("user-123", new List { "user@example.com" }); // Set preferred language Pushwoosh.Instance.SetLanguage("en"); ``` ### Tags Tags are key-value pairs assigned to devices, enabling user segmentation and targeted messaging: ```csharp // String tag Pushwoosh.Instance.SetStringTag("favorite_category", "electronics"); // Integer tag Pushwoosh.Instance.SetIntTag("purchase_count", 5); // List tag Pushwoosh.Instance.SetListTag("interests", new List { "sports", "music", "tech" }); // Get all tags Pushwoosh.Instance.GetTags((tags, error) => { if (error != null) { Debug.Log("Error: " + error.Message); return; } foreach (var tag in tags) { Debug.Log(tag.Key + ": " + tag.Value); } }); ``` ### Events Track user actions to analyze behavior and trigger automated messages: ```csharp // Track a login event Pushwoosh.Instance.PostEvent("login", new Dictionary { { "username", "user-123" }, { "login_type", "email" } }); // Track a purchase event Pushwoosh.Instance.PostEvent("purchase", new Dictionary { { "product_id", "SKU-001" }, { "price", 29.99 }, { "currency", "USD" } }); ``` ### Communication preferences Allow users to opt in or out of push notifications programmatically: ```csharp // Enable communication Pushwoosh.Instance.SetCommunicationEnabled(true); // Disable communication Pushwoosh.Instance.SetCommunicationEnabled(false); // Check current state bool isEnabled = Pushwoosh.Instance.IsCommunicationEnabled(); ``` ### Badge management Control the app badge number on supported platforms: ```csharp // Set badge to a specific number Pushwoosh.Instance.SetBadgeNumber(3); // Increment badge Pushwoosh.Instance.AddBadgeNumber(1); // Clear badge Pushwoosh.Instance.SetBadgeNumber(0); ``` ## Troubleshooting If a push isn’t delivered, find the error code for your platform in [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) to see what it means and how to fix it. If you encounter any issues during the integration process, refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # Unity SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/quick-start/ Get started with the Pushwoosh Unity SDK using this quick and simple example. --- Get started with the Pushwoosh Unity SDK using this quick and simple example. ## Prerequisites To integrate the Pushwoosh Unity SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login). * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * **For iOS integration:** * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * **For Android integration:** * A [configured Android platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * The `project number`(also known as Sender ID) , `google-services.json` file and `package name` from your Firebase project. * A Firebase project connected to your Android application. Follow the [Firebase setup guide](https://firebase.google.com/docs/android/setup#manually_add_firebase) if needed. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project Clone the example project repository: ```bash git clone https://github.com/Pushwoosh/pushwoosh-unity-sample.git ``` ## Configure the project Open **Assets/Scripts/PushNotificator.cs** and set the Pushwoosh initialization values: Assets/Scripts/PushNotificator.cs ```csharp Pushwoosh.ApplicationCode = "XXXXX-XXXXX"; Pushwoosh.FcmProjectNumber = "XXXXXXXXXXXX"; ``` Replace the `ApplicationCode` and `FcmProjectNumber` values with your **Pushwoosh App Code** and **Firebase Project Number**. ### iOS ###### 1. Set the Bundle Identifier In Unity, go to **Edit > Project Settings > Player > iOS > Other Settings** and set the **Bundle Identifier** to your bundle ID (e.g., `com.example.pushwoosh`). ###### 2. Build and install dependencies The example project includes a Pushwoosh Unity SDK version earlier than 6.3.0, with the native iOS SDK 6.7.18. It gets the native iOS SDK with CocoaPods through External Dependency Manager for Unity (EDM4U). Build the iOS project from Unity. Then navigate to the generated Xcode project directory and install the CocoaPods dependencies: ```bash cd ios && pod install ``` In your own project with Pushwoosh Unity SDK 6.3.0 or later, the native iOS SDK is added with Swift Package Manager by default, and you don’t run `pod install`. See [Choose how the iOS SDK is added](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/integration/basic-integration-guide/#3-choose-how-the-ios-sdk-is-added). ###### 3. Set the API Token Open the **ios/Unity-iPhone.xcworkspace** file in Xcode. In your `Info.plist` add the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token): Info.plist ```xml Pushwoosh_API_TOKEN __PUSHWOOSH_DEVICE_API_TOKEN__ ``` ### Android ###### 1. Set the package name In Unity, go to **Edit > Project Settings > Player > Android > Other Settings** and set the **Package Name** to your app’s package name (e.g., `com.example.pushwoosh`). ###### 2. Add google-services.json Copy your `google-services.json` file to the **Assets** directory in your Unity project. ###### 3. Set the API Token In your `Assets/Plugins/Android/AndroidManifest.xml` add the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) inside the `` tag: AndroidManifest.xml ```xml ``` Caution Be sure to give the token access to the correct app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ## Run the project 1. Build and run the project. 2. Tap the **Register for push notifications** button. 3. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Send a push notification Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. --- # Setting up badges for Unity **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/unity/setting-up-badges-for-unity/ This guide describes the process of implementing Badges for Pushwoosh Unity plugin --- Since the Unity plugin does not contain the PWNotificationExtensionManager header, you have to perform several additional steps to implement Badges for your Unity project. Note To implement badges for iOS Native app, please refer to the [Setting up Badges](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges/) guide.. ### 1. Add Notification Service Extension (File -> New -> Target…) ### 2. Select the “Notification Service Extension” ### 3. Add Pushwoosh.xcframework to NotificationService extension target as follows: 1. Choose **NotificationService** extension target. 2. Go to the “**General**” tab. 3. Find ‘**Framework and Libraries**’ and press ’**+**’ button. 4. Select **Pushwoosh.xcframework.** ### 4. Update the Notification Service Extension code as follows: ```plaintext #import "NotificationService.h" #import "PWNotificationExtensionManager.h" @interface NotificationService () @property (nonatomic, strong) void (^contentHandler)(UNNotificationContent *contentToDeliver); @property (nonatomic, strong) UNMutableNotificationContent *bestAttemptContent; @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { self.contentHandler = contentHandler; self.bestAttemptContent = [request.content mutableCopy]; [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } - (void)serviceExtensionTimeWillExpire { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. self.contentHandler(self.bestAttemptContent); } @end ``` ### 5. Add the “App Groups Capability” for each target of your application ![App Groups Capability](https://docs.pushwoosh.com/cross-platform-frameworks-setting-up-badges-for-unity-3.webp) ![Info.plist](https://docs.pushwoosh.com/cross-platform-frameworks-setting-up-badges-for-unity-4.webp) ### 6. Add the App Groups ID to your info.plist for each target of your application ```plaintext PW_APP_GROUPS_NAME group.com.example.demoapp_example ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # iOS SDK Overview **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ Explore Pushwoosh iOS SDK docs. Find integration guides, API reference, examples & support to add push notifications to your iOS app --- ## Setting up iOS SDK Choose the integration method that best suits your needs to get started with the Pushwoosh iOS SDK: * [AI-Assisted Integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ai-integration/) - use AI coding assistants to integrate and configure the Pushwoosh iOS SDK Or doing by yourself: * [Quick start guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/quick-start/) - set up basic push notifications in minutes with quick and simple example * [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/) - a step-by-step approach to setting up the iOS SDK from scratch * [Advanced integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/advanced-integration-guide/) - unlock the full potential of the Pushwoosh iOS SDK with advanced integration techniques ## Feature flags [Feature flags](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/) - read the flags set in the Control Panel and switch features in your app per user ## Technical Documentation [iOS SDK API Reference](https://pushwoosh.github.io/pushwoosh-ios-sdk/) — complete technical documentation covering all public classes, methods, and properties ## SDK Customization [Learn how to customize iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/) ## Code examples [Let’s explore Pushwoosh functionality](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/pushwoosh-sdk-samples-for-ios/) without integrating the SDK into your project ## Troubleshooting and FAQ [Explore common issues and solutions](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-faq/) ## Support and community Have questions or problems with the Pushwoosh SDK? [Reach out to our support team!](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community/) ## SDK module sizes The table below shows the download sizes of each SDK module (version 7.0.12). These are the compressed sizes of the XCFramework archives. | Module | Size | Description | | ----------------------- | ------- | --------------------------------------------------- | | PushwooshFramework | 422 KB | Main SDK framework (required) | | PushwooshCore | 1.93 MB | Core functionality (required) | | PushwooshBridge | 394 KB | Bridge module (required) | | PushwooshLiveActivities | 314 KB | Live Activities & Dynamic Island support (required) | | PushwooshForegroundPush | 1.40 MB | Foreground push notifications | | PushwooshGeozones | 130 KB | Geolocation-based push notifications | | PushwooshVoIP | 302 KB | VoIP push notifications | | PushwooshInboxUI | 299 KB | Message inbox UI components | | PushwooshTVOS | 1.05 MB | tvOS support | App Store size The actual size added to your app may differ from the values above. The App Store uses additional compression and app thinning, which typically results in a smaller download size for end users. The sizes shown are for the raw XCFramework archives. ## References [iOS SDK API Reference ](https://pushwoosh.github.io/pushwoosh-ios-sdk/)Complete technical documentation covering all public classes, methods, and properties. [iOS SDK Source Code ](https://github.com/Pushwoosh/pushwoosh-ios-sdk)Browse the open-source iOS SDK repository on GitHub. [iOS Code Examples ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/pushwoosh-sdk-samples-for-ios/)Sample code and demos to explore Pushwoosh iOS SDK functionality. --- # AI-Assisted Integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ai-integration/ Integrate Pushwoosh iOS SDK using AI coding assistants like Claude Code, Cursor, or GitHub Copilot with Context7 MCP --- Use AI coding assistants to integrate and configure the Pushwoosh iOS SDK in your project. By connecting your assistant to the [Context7](https://context7.com/) MCP server, it gets access to up-to-date SDK documentation, code examples, and API reference — so it can generate accurate, working integration code. Note Your AI assistant must support [MCP (Model Context Protocol)](https://modelcontextprotocol.io/) to use Context7. Supported tools include **Claude Code**, **Cursor**, **Windsurf**, **Cline**, and others. ## How it works Context7 is an MCP server that provides AI assistants with up-to-date library documentation. When your assistant has access to Context7, it can fetch the latest Pushwoosh iOS SDK docs, code snippets, and API reference directly during the conversation — instead of relying on potentially outdated training data. The Pushwoosh iOS SDK library ID on Context7: **`/pushwoosh/pushwoosh-ios-sdk`** ## Setting up Context7 Add [Context7](https://github.com/upstash/context7#installation) to your AI assistant’s MCP configuration: * Claude Code Run the following command: ```bash claude mcp add --scope user context7 -- npx -y @upstash/context7-mcp --api-key YOUR_API_KEY ``` * Cursor / Windsurf Add to your MCP config file (`.cursor/mcp.json` or equivalent): ```json { "mcpServers": { "context7": { "command": "npx", "args": ["-y", "@upstash/context7-mcp@latest"] } } } ``` ## Example prompts Once Context7 is configured, you can use natural language prompts to integrate the SDK. Below are ready-to-use prompts for common tasks. ### Basic SDK integration ```text Integrate Pushwoosh iOS SDK into my iOS project using Swift Package Manager. Requirements: - Add SPM dependency from https://github.com/Pushwoosh/Pushwoosh-XCFramework - Configure Info.plist with Pushwoosh App ID: YOUR_APP_ID - Register for push notifications in AppDelegate Use Context7 MCP to fetch Pushwoosh iOS SDK documentation. ``` ### Push notification handling ```text Show me how to handle push notification callbacks (receive, open) with Pushwoosh SDK in iOS. I want to implement PWMessagingDelegate and add analytics tracking for these events. Use Context7 MCP to fetch Pushwoosh iOS SDK documentation for PWMessagingDelegate. ``` ### User segmentation with tags ```text Show me how to use Pushwoosh tags for user segmentation in iOS. Create example helper class with methods for setting and getting tags. Use Context7 MCP to fetch Pushwoosh iOS SDK documentation for setTags and getTags. ``` Tip Adding **“Use Context7 MCP to fetch Pushwoosh iOS SDK documentation”** at the end of your prompt tells the AI assistant to pull the latest SDK docs before generating code. ## What the AI assistant gets When your assistant queries Context7 for the Pushwoosh iOS SDK, it receives: * **Installation instructions** — SPM and CocoaPods setup * **API reference** — public classes, methods, properties, and protocols * **Code examples** — Swift and Objective-C snippets for common use cases * **Module documentation** — guides for each SDK module (VoIP, Live Activities, Geozones, etc.) * **Configuration details** — Info.plist keys, entitlements, and capabilities setup --- # Customizing iOS SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/ Customize Pushwoosh iOS SDK to meet your goals and preferences --- ## Integration * [Deep linking](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#deep-linking) * [Universal Links](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#universal-links) * [In-app purchase tracking](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#in-app-purchase-tracking) * [Geozones push notifications](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#geozones-push-notifications) * [Creating a Rich Media queue](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#creating-a-rich-media-queue) * [Autoplay a video sent in a Rich Notification with force touch](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#autoplay-a-video-sent-in-a-rich-notification-with-force-touch) * [Custom push sound](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#custom-push-sound) * [iOS Provisional Push](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#ios-provisional-push) ### Deep linking In your **Info.plist** file add `URL types` array with `URL Identifier` and `URL Scheme`.\ In the example below the `URL Scheme` is *com.pushwoosh* and the `URL Identifier` is *promotion*. In your App Delegate file (usually AppDelegate.m for iOS 12 and below, or SceneDelegate.m for iOS 13 and above), add the appropriate openURL delegate function as outlined in the example below. The example checks for the correct page, parses the “id” value from the URL, and opens PromoPageViewController in response. `AppDelegate.swift` * Swift ```swift func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let page = components?.host var promotionId: String? if page == "promotion" { return } let items = components?.queryItems ?? [] for item in items { if item.name == "id" { promotionId = item.value } } //show PromoPageViewController } ``` * Objective-C ```objective-c - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation { NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; NSString *page = components.host; NSString *promotionId = nil; //return if this is not a promotion deep link if(![page isEqualToString:@"promotion"]) return NO; for(NSURLQueryItem *item in components.queryItems) { if([item.name isEqualToString:@"id"]) promotionId = item.value; } PromoPageViewController *vc = [[PromoPageViewController alloc] init]; vc.promotionId = promotionId [self presentViewController:vc animated:YES completion:nil]; } ``` `SceneDelegate.swift` * Swift ```swift func scene(_ scene: UIScene, openURLContexts URLContexts: Set) { guard let urlContext = URLContexts.first else { return } let components = URLComponents(url: urlContext.url, resolvingAgainstBaseURL: false) let page = components?.host var promotionId: String? guard page == "promotion" else { return } let items = components?.queryItems ?? [] for item in items { if item.name == "id" { promotionId = item.value } } //show PromoPageViewController } ``` * Objective-C ```objective-c - (void)scene:(UIWindowScene *)scene openURLContexts:(NSSet *)URLContexts { UIOpenURLContext *urlContext = URLContexts.anyObject; if (!urlContext) { return; } NSURLComponents *components = [NSURLComponents componentsWithURL:urlContext.URL resolvingAgainstBaseURL:NO]; NSString *page = components.host; NSString *promotionId = nil; if (![page isEqualToString:@"promotion"]) { return; } for (NSURLQueryItem *item in components.queryItems) { if ([item.name isEqualToString:@"id"]) { promotionId = item.value; } } //show PromoPageViewController } ``` ### Universal Links Universal Links allow users to open your app directly when they tap a link to your website. Unlike custom URL schemes, Universal Links use standard `https://` URLs and provide a more seamless user experience. Note Starting from **SDK version 7.0.15**, Pushwoosh iOS SDK automatically routes Universal Links from push notifications to your app’s handlers instead of opening them in Safari. #### How it works When a push notification contains an `https://` URL (in the “url” or “l” parameter), the SDK will: 1. Create an `NSUserActivity` with the URL 2. Call your app’s Universal Links handler (`scene:continueUserActivity:` or `application:continueUserActivity:restorationHandler:`) 3. If your app doesn’t handle the URL, it will open in Safari as a fallback #### Setup 1. **Configure Associated Domains in Xcode** Add the Associated Domains capability to your app and add your domain: ```plaintext applinks:yourdomain.com ``` 2. **Host the Apple App Site Association file** Create an `apple-app-site-association` file on your web server at `https://yourdomain.com/.well-known/apple-app-site-association`: ```json { "applinks": { "apps": [], "details": [ { "appID": "TEAM_ID.com.your.bundleid", "paths": ["/path/*", "/promotion/*"] } ] } } ``` Replace `TEAM_ID` with your Apple Developer Team ID and `com.your.bundleid` with your app’s bundle identifier. 3. **Implement the Universal Links handler** `SceneDelegate.swift` (iOS 13+) * Swift ```swift func scene(_ scene: UIScene, continue userActivity: NSUserActivity) { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return } // Handle the Universal Link URL let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let path = components?.path if path?.starts(with: "/promotion") == true { // Navigate to promotion screen let promotionId = components?.queryItems?.first(where: { $0.name == "id" })?.value // Show promotion with promotionId } } ``` * Objective-C ```objective-c - (void)scene:(UIScene *)scene continueUserActivity:(NSUserActivity *)userActivity { if (![userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { return; } NSURL *url = userActivity.webpageURL; if (!url) { return; } // Handle the Universal Link URL NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; NSString *path = components.path; if ([path hasPrefix:@"/promotion"]) { // Navigate to promotion screen NSString *promotionId = nil; for (NSURLQueryItem *item in components.queryItems) { if ([item.name isEqualToString:@"id"]) { promotionId = item.value; break; } } // Show promotion with promotionId } } ``` `AppDelegate.swift` (iOS 12 and earlier, or as fallback) * Swift ```swift func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([UIUserActivityRestoring]?) -> Void) -> Bool { guard userActivity.activityType == NSUserActivityTypeBrowsingWeb, let url = userActivity.webpageURL else { return false } // Handle the Universal Link URL let components = URLComponents(url: url, resolvingAgainstBaseURL: false) let path = components?.path if path?.starts(with: "/promotion") == true { // Navigate to promotion screen return true } return false } ``` * Objective-C ```objective-c - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray> *restorableObjects))restorationHandler { if (![userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) { return NO; } NSURL *url = userActivity.webpageURL; if (!url) { return NO; } // Handle the Universal Link URL NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO]; NSString *path = components.path; if ([path hasPrefix:@"/promotion"]) { // Navigate to promotion screen return YES; } return NO; } ``` #### Sending push with Universal Link When creating a push notification, use your website URL in the **Action** field: ```plaintext https://yourdomain.com/promotion?id=123 ``` The SDK will automatically route this URL to your Universal Links handler, allowing you to navigate the user to the appropriate screen in your app. Tip Universal Links provide better user experience than custom URL schemes because they work even if your app isn’t installed — the user will be taken to your website instead. ### In-app purchase tracking By default, tracking of in-app purchases is disabled. If you want to track in-app purchases when configuring [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview), set the *Pushwoosh_PURCHASE_TRACKING_ENABLED* flag to *true* in the *info.plist* file. You can find a list of available flags in the [table](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/advanced-integration-guide/#complete-list-of-infoplist-properties). If you want to track in-app purchases manually, you can use the code below. In `paymentQueue:updatedTransactions: delegate` method call `sendSKPaymentTransactions method` of `PushManager` * Swift ```swift func paymentQueue(_ queue: SKPaymentQueue, updatedTransactions transactions: [SKPaymentTransaction]) { // In-Apps Tracking Pushwoosh code here Pushwoosh.sharedInstance().sendSKPaymentTransactions(transactions) // the rest of the code, consume transactions, etc } ``` * Objective-C ```objective-c - (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions { [[PushNotificationManager pushManager] sendSKPaymentTransactions:transactions]; //the rest of the code, consume transactions, etc } ``` ![InAppTrackingViewController.swift](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-4.webp) [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/In-Apps%20Tracking/InAppTrackingViewController.swift) ### Geozones push notifications Geozones push notifications are encapsulated into a separate framework **PushwooshGeozones**. 1. Add PushwooshGeozones.framework to your project To add PushwooshGeozones.framework to your project using a dependency manager, put the following lines in your `podfile` or `cartfile`: * Podfile ```plaintext pod 'PushwooshXCFramework/Geozones' ``` * Carfile ```plaintext github "Pushwoosh/pushwoosh-ios-sdk" ``` * SwiftPM If you want to use **PushwooshGeozones.xcframework**, enter the following Package URL:\ [PushwooshGeozones-XCFramework](https://github.com/Pushwoosh/PushwooshGeozones-XCFramework) Alternatively, you can simply drag and drop the framework into **Link Binaries With Libraries** in your project’s **Build Phases**. 2. Add the following keys to your Info.plist: * **NSLocationWhenInUseUsageDescription** – *(required)* for the app to track Geozones only while running in the foreground. * **NSLocationAlwaysAndWhenInUseUsageDescription** – *(required)* for the app to track Geozones **in both foreground and background** and to show a permission request dialog pop-up. * **NSLocationAlwaysUsageDescription** – *(optional)* for the app to track Geozones **at all times**; should be used if your app targets iOS 10 and earlier versions. ```xml NSLocationAlwaysAndWhenInUseUsageDescription for app to track Geozones in both conditions and to show a permission request dialog NSLocationWhenInUseUsageDescription for app to track Geozones only while running in the foreground ``` [Pushwoosh Info.plist example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Info.plist) 3. Import the framework * Swift ```swift import PushwooshGeozones ``` * Objective-C ```objc #import ``` 4. Start Geozones tracking * Swift ```swift PWGeozonesManager.shared()?.startLocationTracking() ``` [GitHub reference](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/Geozones%20Push%20Notification/GeozonesViewController.swift) * Objective-C ```objc [[PWGeozonesManager sharedManager] startLocationTracking]; ``` [GitHub reference](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Geozones%20Push%20Notification/PWGeozonesViewController.m) ##### Example * Swift Example ```swift override func viewDidLoad() { super.viewDidLoad() // Start Geozones tracking when needed PWGeozonesManager.shared().startLocationTracking() } ``` * Objective-C Example ```objc - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // Start Geozones tracking when needed [[PWGeozonesManager sharedManager] startLocationTracking]; } ``` ### Creating a Rich Media queue In case there are several Rich Media pages to display simultaneously (for example, trigger events for two or more In-Apps take place at one moment, or a Rich Media page is being displayed already at the moment a different trigger event occurs), you can set up a queue for Rich Media pages displaying. To create a queue, follow the steps described below. 1. Create a class that implements PWRichMediaPresentingDelegate: ```objective-c @interface ChainedRichMediaPresentingDelegate () @property (nonatomic) NSMutableArray *queue; @property (nonatomic) BOOL inAppIsPresenting; @end @implementation ChainedRichMediaPresentingDelegate - (instancetype)init { self = [super init]; if (self) { _queue = [NSMutableArray new]; } return self; } - (BOOL)richMediaManager:(PWRichMediaManager *)richMediaManager shouldPresentRichMedia:(PWRichMedia *)richMedia { [_queue addObject:richMedia]; return !_inAppIsPresenting; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didPresentRichMedia:(PWRichMedia *)richMedia { _inAppIsPresenting = YES; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didCloseRichMedia:(PWRichMedia *)richMedia { _inAppIsPresenting = NO; [_queue removeObject:richMedia]; if (_queue.count) { [[PWRichMediaManager sharedManager] presentRichMedia:_queue.firstObject]; } } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager presentingDidFailForRichMedia:(PWRichMedia *)richMedia withError:(NSError *)error { [self richMediaManager:richMediaManager didCloseRichMedia:richMedia]; } @end ``` 2. Set the delegate: ```objective-c [PWRichMediaManager sharedManager].delegate = [ChainedRichMediaPresentingDelegate new]; ``` * Swift (GitHub) [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/customizing/customizing/Rich%20Media%20Queue) * Objective-C (GitHub) [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/customizing/customizingObjC/customizingObjC/Rich%20Media%20Queue) ### Autoplay a video sent in a Rich Notification with force touch To make a video sent as a [Rich Notification attachment](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-rich-notifications-integration/) autoplay when the notification is expanded without any user interaction, follow the steps below: 1. Add the Notification Content Extension to your project: * In Xcode, select File > New > Target. * Choose Notification Content Extension. * Assign it a name and complete the setup. ![Notification Content Extension - iOS Rich Push Notification](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-5.webp) If prompted with the “Activate scheme” message, choose Cancel. ![Activate Notification Content Scheme](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-6.webp) 2. Adjust the properties and methods in the Content Extension as follows: ```plaintext import UIKit import UserNotifications import UserNotificationsUI import AVKit class NotificationViewController: UIViewController, UNNotificationContentExtension { var playerController: AVPlayerViewController! @IBOutlet weak var playerBackgroundView: UIView! override func viewDidLoad() { super.viewDidLoad() // Do any required interface initialization here. } func didReceive(_ notification: UNNotification) { let attachment = notification.request.content.attachments.first playerController = AVPlayerViewController() // Set height programmatically // preferredContentSize.height = 250 if let url = attachment?.url { setupVideoPlayer(url: url) } else { print("No valid URL...") } } private func setupVideoPlayer(url: URL) { guard let playerController = self.playerController else { return } let player = AVPlayer(url: url) playerController.player = player playerController.view.frame = self.playerBackgroundView.bounds playerBackgroundView.addSubview(playerController.view) addChild(playerController) playerController.didMove(toParent: self) player.play() } ``` 3. Incorporate a UIView into MainInterface.storyboard: ![UIView in the MainInterface.storyboard](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-7.webp) 4. Link the playerBackgroundView IBOutlet with the UIView you just added: ![Link the playerBackgroundView IBOutlet with UIView](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-8.webp) 5. Update the info.plist file with the following entry: ```plaintext UNNotificationExtensionUserInteractionEnabled = true ``` ![UNNotificationExtensionUserInteractionEnabled = true](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-9.webp) In order to attach a video to your notification, input a video’s URL to the Media Attachment field in the Control Panel: ![Video’s URL in the Media Attachment field in Pushwoosh Control Panel](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-10.webp) When sending a notification via API [/createMessage](https://docs.pushwoosh.com/developer/api-reference/messages-api#createmessage) request, include the URL in the “ios_attachment” parameter and ensure the “mutable-content” flag is set to \`1\`. Note To test the autoplay feature, you can use the following sample video: [Hello World Sample Video](https://docs-assets.developer.apple.com/published/efa8e7a0a97cfab20bf0f4c307b9b121/Hello-World-overview.mp4). [](https://docs.pushwoosh.com/ios-push-notifications-customizing-ios-sdk-11.webm "Hello World Sample Video") ### Custom push sound To play a custom sound when on a push notification receiving, first put the audio file into your project’s root folder. Then, specify the sound file’s name in push parameters – fill in the Sound field of the [iOS-specific settings](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push#ios) of your message or specify the file name as a value for the “ios_sound” param of the [createMessage API request](https://docs.pushwoosh.com/developer/api-reference/messages-api/). Audio file for custom iOS sound has to be in one of the following formats: **.aif**, **.caf**, **.wav**. **Make sure to specify the format in the file’s name; otherwise, it will be ignored by Pushwoosh iOS SDK.** Tip Consider App Store limitations on bundle size when adding the sound file to your bundle. ### iOS Provisional Push Note Supported on iOS 12 and later. #### How it works Provisional push notifications appear silently in the user’s Notification Center but not on the lock screen. This type of pushes doesn’t need to be allowed by a user explicitly: you can start sending them as soon as a user installs and launches your app. However, users still can subscribe to your prominent push notifications: when opening the Provisional Push, they have two options to choose their experience – to keep pushes in Notification Center without alerts and sounds or allow you to send pushes prominently so that they appear on the lock screen. Provisional Pushes are designed to let users make informed decisions about whether they’d like to receive notifications from your app. As the APN native subscription request is shown to users only once and to subscribe later, they should go to their phone’s system settings, and some users might not subscribe since they aren’t aware of what value they get with your pushes. Provisional Pushes give users this understanding: they can see what content you deliver in push notifications and decide whether they need to be notified about this content prominently. Important Please note that Provisional Pushes, when implemented, replace the native APN prompt, which means users cannot subscribe to prominent notifications until they open their Notification Center and choose to receive Provisional Pushes as prominent ones. #### How to implement 1. Integrate the Pushwoosh iOS SDK by following the [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/quick-start/). 2. Add the following string to your project’s AppDelegate before calling the `registerForPushNotifications()` method: * Swift ```swift if #available(iOS 12.0, *) { Pushwoosh.sharedInstance().additionalAuthorizationOptions = UNAuthorizationOptions.provisional } ``` [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/provisionalpush/provisionalpush/AppDelegate/AppDelegate.swift) * Objective-C ```objective-c if (@available(iOS 12.0, *)) { [Pushwoosh sharedInstance].additionalAuthorizationOptions = UNAuthorizationOptionProvisional; } ``` [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/provisionalpush/provisionalpushObjC/provisionalpushObjC/AppDelegate/AppDelegate.m) That’s it! App users will receive messages directly to their Notification Center once they install the app. ### Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Feature flags in the iOS SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/ Read Pushwoosh feature flags and their properties in your iOS app, react to updates, and log impressions for comparison. --- `Pushwoosh.configure.featureFlags` gives your iOS app the [feature flags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) configured for the application in the Control Panel. Pushwoosh evaluates the flag rules on the server for the current device and user, and the SDK returns the result: whether the flag is on for this device and which property values apply. Beta feature Feature flags are in beta and available only on plans that include them. On other plans the server returns no flags, so every flag falls back to the default you pass in code. ## Prerequisites * Pushwoosh iOS SDK 7.2.10 or later, integrated as described in the [iOS SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/). Feature flags ship in the required `PushwooshFramework` and `PushwooshCore` modules, so you don’t add another module. They work in the iOS app itself. App extensions, such as a Notification Service Extension, never request flags. * At least one flag created in **Audience** → **Feature Flags**. You need its **Key** and the keys and types of its properties. Use the keys exactly as they appear there, letter case included. For example, `get("New_Checkout")` doesn’t find a flag with the key `new_checkout`. * If the app sends SDK requests through a reverse proxy, its URL must end with `json/1.3/`. Otherwise the SDK doesn’t request flags, and every flag falls back to your default. ## Read a flag Always pass a default. It applies until the SDK has flags for the device, and whenever the flag doesn’t exist, is archived, or the plan doesn’t include feature flags. ```swift import PushwooshFramework let flags = Pushwoosh.configure.featureFlags if let checkout = flags.get("new_checkout"), checkout.isEnabled { showNewCheckout( buttonColor: checkout.string(forProperty: "button_color", default: "blue") ?? "blue", maxItems: Int(checkout.number(forProperty: "max_items", default: 3)) ) } else { showOldCheckout() } ``` To check only whether the flag is on, use `isEnabled`: ```swift let isNewCheckout = Pushwoosh.configure.featureFlags.isEnabled("new_checkout", default: false) ``` In Objective-C, call `[PushwooshConfig featureFlags]`. ## What a flag returns `get(_:)` returns a `PWFeatureFlag` object, or `nil` when the SDK has no flags for the device yet or the flag isn’t in the answer. An archived flag is not in the answer. `get` reads only the stored flags and never waits for the network. The object has these properties: | Property | Type | Description | | :---------- | :----- | :-------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | String | The flag key. | | `isEnabled` | Bool | Whether the flag is on for this device. | | `ruleId` | String | The rule that decided the result, for example `r_1a2b3c4d`. `default` when no rule matched the device, and an empty string when the flag is killed. | Read property values with the method that matches the property type. When the flag is off, the properties hold the default values set in the Control Panel. | Method | Returns | | :----------------------------- | :-------------------------------------------------------------------------------- | | `string(forProperty:default:)` | The value of a **String** property, or your default. | | `number(forProperty:default:)` | The value of a **Number** property as a `Double`, or your default. | | `bool(forProperty:default:)` | The value of a **Boolean** property, or your default. | | `jsonObject(forProperty:)` | The value of a **JSON** property that holds an object, as a dictionary, or `nil`. | | `jsonArray(forProperty:)` | The value of a **JSON** property that holds an array, or `nil`. | Each method returns your default, or `nil` for the JSON methods, when the property is missing or holds a value of another type. `isEnabled(_:default:)` returns the default when `get(_:)` returns `nil`. ## Keep flags up to date The SDK stores the last answer on the device for up to 24 hours. The stored flags are valid only for the User ID and the Application Code of the app they were requested for. The SDK refreshes them: * When a session starts, that is, when the app becomes active for the first time or after at least 30 minutes in the background. * Every hour while the app is in the foreground. * After the device registers for push notifications, if the previous request was sent before the registration. * Right after the User ID changes with `setUserId`. The stored flags of the previous user are discarded, so a new user never sees them. To react to new values, add a listener that adopts `PWFeatureFlagsUpdateListener`. Its `featureFlagsDidUpdate()` is called on the main thread after a refresh that changed any flag. The SDK holds listeners weakly. Keep a strong reference to yours, or it’s released and never called: ```swift final class CheckoutViewController: UIViewController, PWFeatureFlagsUpdateListener { override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) Pushwoosh.configure.featureFlags.addUpdateListener(self) } override func viewWillDisappear(_ animated: Bool) { Pushwoosh.configure.featureFlags.removeUpdateListener(self) super.viewWillDisappear(animated) } func featureFlagsDidUpdate() { render() } } ``` To request the flags yourself, for example after an action that may move the user into a different segment, call `refresh(completion:)`. While the stored flags are valid, it sends at most one request per 5 minutes and answers from the stored flags within that window. The completion runs on the main thread: ```swift Pushwoosh.configure.featureFlags.refresh { error in if let error = error as NSError? { let status = error.code // the stored flags, or your defaults, stay in effect } render() } ``` On failure, the error has the `PWFeatureFlagsErrorDomain` domain, and its `code` is the HTTP status. The code is `0` when the request got no HTTP response or wasn’t sent, and `200` when the server answered but the answer couldn’t be used. To test refreshes without the 5-minute limit, set the `Pushwoosh_FEATURE_FLAGS_DISABLE_THROTTLE` Boolean key in `Info.plist` to `YES`. If Pushwoosh can’t match the device’s segments in time, it returns a partial answer. The SDK doesn’t store it on the device, and the next refresh brings the full answer. When a flag is killed in the Control Panel, devices get it off with default property values at the next refresh. When the SDK can’t reach the server, it keeps using the stored flags until they are 24 hours old, and your defaults after that. ## Log impressions An impression tells Pushwoosh that a user actually reached the code a flag controls, so you can compare users who got the feature with those who didn’t. Call `logImpression(_:)` at the moment the user sees the feature or its old version, for both the enabled and the disabled branch, because users with the flag off are your comparison group: ```swift let flags = Pushwoosh.configure.featureFlags let isOn = flags.isEnabled("new_checkout", default: false) flags.logImpression("new_checkout") if isOn { showNewCheckout() } else { showOldCheckout() } ``` The SDK posts the `PW_FeatureFlagImpression` event with these attributes: | Attribute | Type | Value | | :--------- | :------ | :------------------------------------------------ | | `flag_key` | string | The flag key. | | `rule_id` | string | The rule that decided the result, as in `ruleId`. | | `enabled` | boolean | Whether the flag was on. | You don’t need to create the event in advance, because Pushwoosh adds `PW_FeatureFlagImpression` with these attributes to **Audience** → **Events** when the first impression arrives. The SDK sends one impression per flag, rule, and state in a session, and sends nothing when there is no flag for the key. If sending fails, the next `logImpression` call for the flag sends it again. Impressions also let marketers build a segment of users who reached the feature: the **saw the feature (30 days)** state of the [feature flag condition](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature). ## Method reference `Pushwoosh.configure.featureFlags` has these methods: | Method | Description | | :------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `get(_:)` | Returns the `PWFeatureFlag` for the key, or `nil`. Reads the stored flags only. | | `isEnabled(_:default:)` | Returns whether the flag is on, or the default when there is no flag. | | `refresh(completion:)` | Requests the flags, throttled to once per 5 minutes while the stored flags are valid. The completion gets `nil` or an error in `PWFeatureFlagsErrorDomain`, and can be `nil`. | | `addUpdateListener(_:)` | Calls the listener’s `featureFlagsDidUpdate()` after every refresh that changed a flag. The listener is held weakly. | | `removeUpdateListener(_:)` | Removes a listener added with `addUpdateListener(_:)`. | | `logImpression(_:)` | Posts a `PW_FeatureFlagImpression` event for the flag’s current rule and state. | --- # iOS App Clips **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-app-clips/ Send push notifications to App Clip users with Pushwoosh SDK — ephemeral permissions, no app install required. --- App Clips let users complete a task quickly without downloading your full app. With Pushwoosh, you can send push notifications to App Clip users — iOS automatically grants **8-hour notification access** when the App Clip is opened. ## What you need * Pushwoosh iOS SDK **7.0.33+** * Xcode 15+ * A **separate Pushwoosh application** for your App Clip (different bundle ID = separate app) * A real device for testing (push tokens don’t work on simulator) ## 1. Create a Pushwoosh application Your App Clip has a different bundle ID (e.g. `com.yourapp.Clip`), so it needs a **separate Pushwoosh app** with its own APNs key. 1. Go to [Pushwoosh Control Panel](https://app.pushwoosh.com/) 2. Create a new application with the App Clip bundle ID 3. Upload your APNs authentication key 4. Copy the **Application Code** and **API Token** ## 2. Create an App Clip target 1. In Xcode: **File → New → Target → App Clip** 2. Add **Push Notifications** capability to the App Clip target For detailed screenshots of adding capabilities, see the [basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/). ## 3. Add Pushwoosh frameworks In the App Clip target’s **Build Phases → Link Binary With Libraries**, add: * `PushwooshFramework.framework` * `PushwooshCore.framework` * `PushwooshBridge.framework` ## 4. Configure Info.plist ```xml Pushwoosh_APPID XXXXX-XXXXX Pushwoosh_API_TOKEN YOUR_API_TOKEN NSAppClip NSAppClipRequestEphemeralUserNotification NSAppClipRequestLocationConfirmation UIBackgroundModes remote-notification ``` ## 5. Initialize the SDK * SwiftUI ```swift import SwiftUI import PushwooshFramework class AppClipDelegate: NSObject, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { Pushwoosh.configure.delegate = self Pushwoosh.configure.registerForPushNotifications() return true } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.configure.handlePushRegistration(deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.configure.handlePushRegistrationFailure(error as NSError) } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.configure.handlePushReceived(userInfo) completionHandler(.noData) } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("Push opened: \(message.payload ?? [:])") } } @main struct YourAppClip: App { @UIApplicationDelegateAdaptor(AppClipDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } ``` * UIKit ```swift import UIKit import PushwooshFramework @main class AppDelegate: UIResponder, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Pushwoosh.configure.delegate = self Pushwoosh.configure.registerForPushNotifications() return true } func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.configure.handlePushRegistration(deviceToken) } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.configure.handlePushRegistrationFailure(error as NSError) } func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.configure.handlePushReceived(userInfo) completionHandler(.noData) } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("Push opened: \(message.payload ?? [:])") } } ``` With ephemeral permissions, iOS grants push access for 8 hours automatically. If you want to use ephemeral mode **without showing a permission dialog**, call `UIApplication.shared.registerForRemoteNotifications()` directly instead of `Pushwoosh.configure.registerForPushNotifications()`. To request **permanent** push permission (standard dialog), use `Pushwoosh.configure.registerForPushNotifications()` as shown above. ## Ephemeral push permissions When a user opens your App Clip, iOS grants push notification access for **8 hours** without asking. After 8 hours, the permission expires. The user must reopen the App Clip to get another 8 hours. To get a push token for 8 hours **without showing a permission dialog**: ```swift UIApplication.shared.registerForRemoteNotifications() ``` To get a **permanent** push token (shows the standard permission dialog): ```swift Pushwoosh.configure.registerForPushNotifications() ``` ## Limitations | Limitation | Details | | ---------------------------- | -------------------------------------------------------------------------------------------------- | | **No rich notifications** | App Clips can’t include Notification Service Extension — no images, no Communication Notifications | | **Size limit** | 15 MB (iOS 16) / 50 MB (iOS 17+) | | **8-hour push window** | Ephemeral permission expires after 8 hours | | **No background processing** | Silent push has limited functionality | | **Device ID** | `identifierForVendor` returns zeros — SDK handles this automatically (7.0.33+) | ## How it looks ![App Clip push notification on iPhone](https://docs.pushwoosh.com/ios-appclip-push.png) *Push notification received in an App Clip with ephemeral permissions* --- # iOS custom foreground push notification **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-custom-foreground-push-notification/ Custom foreground push notifications when native system alerts are disabled. --- Starting from version 6.10.0, you can integrate the `PushwooshForegroundPush` module to customize foreground push notifications when native iOS system alerts are disabled. ### 1. Disable native foreground push alerts Adding `Pushwoosh_SHOW_ALERT = false` to your `Info.plist`. ```xml Pushwoosh_SHOW_ALERT ``` ### 2. Integrating the `PushwooshForegroundPush` Module **Swift Package Manager** Important The modules `PushwooshFramework`, `PushwooshCore`, `PushwooshBridge`, and `PushwooshLiveActivities` are **required**. **Cocoapods** ```bash # Uncomment the next line to define a global platform for your project # platform :ios, '13.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'PushwooshXCFramework' pod 'PushwooshFramework/PushwooshForegroundPush' end ``` ### 3. Add `PushwooshForegroundPush` Configuration in AppDelegate ```swift import UIKit import PushwooshFramework import PushwooshForegroundPush @main class AppDelegate: UIResponder, UIApplicationDelegate, PWMessagingDelegate, PWForegroundPushDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Pushwoosh.ForegroundPush.foregroundNotificationWith(style: .style1, duration: 5, vibration: .notification, disappearedPushAnimation: .balls) Pushwoosh.ForegroundPush.delegate = self return true } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { if let payload = message.payload { // Pushwoosh method Pushwoosh.ForegroundPush.showForegroundPush(userInfo: payload) } } } ``` Using `foregroundNotificationWith` Method The foregroundNotificationWith method allows you to display a custom foreground push notification with configurable style, duration, and haptic feedback. Method Signature (Swift / Objective-C): ```swift @objc static func foregroundNotificationWith( style: PWForegroundPushStyle, duration: Int, vibration: PWForegroundPushHapticFeedback, disappearedPushAnimation: PWForegroundPushDisappearedAnimation ) ``` `Parameters:` 1. `style` (`PWForegroundPushStyle`) * Currently, only style1 is available. 2. `duration` (`Int`) * Specifies how long the notification will be displayed before disappearing (in seconds). 3. `vibration` (`PWForegroundPushHapticFeedback`) * Controls the haptic feedback when the notification is shown. Available options: ```swift case none // No vibration case light // Light vibration case medium // Medium vibration case heavy // Heavy vibration case soft // Soft vibration case rigid // Rigid vibration case notification // Standard notification vibration ``` 4. `disappearedPushAnimation` (`PWForegroundPushDisappearedAnimation`) * Push disappearance animation ```swift case balls = 0 case regularPush ``` ### 4. Implementing `didTapForegroundPush` Delegate Method To handle user taps on custom foreground push notifications, implement the `PWForegroundPushDelegate` protocol method: ```swift // Handle tap on foreground push func didTapForegroundPush(_ userInfo: [AnyHashable : Any]) { print("Foreground custom push: \(userInfo)") // Perform any action, e.g., navigate to a specific screen // navigateToScreen(for: userInfo) } ``` Notes: * This method is called when the user taps on a custom foreground push. * userInfo contains the payload of the notification. * Make sure to set `Pushwoosh.ForegroundPush.delegate = self` after configuration. ### 5. Optional Parameters for Customizing Foreground Push Notifications The `PushwooshForegroundPush` module provides several optional parameters to customize the appearance and behavior of your foreground push notifications. These can be set globally via static properties. | Property | Type | Description | Default | | ---------------- | ----------- | -------------------------------------------------------------------------------------------------- | ----------------------- | | useLiquidView | Bool | Use Liquid Glass view on iOS 26. | false | | gradientColors | [UIColor]? | Optional array of colors for a gradient background. | nil | | backgroundColor | UIColor? | Background color for the push. If nil and gradientColors is not set, the default gradient is used. | Default system gradient | | usePushAnimation | Bool | Whether to animate the push when shown. | true | | titlePushColor | UIColor? | Color of the notification title text. Defaults to system white if nil. | white | | messagePushColor | UIColor? | Color of the notification message text. Defaults to system white if nil. | white | | titlePushFont | UIFont? | Font of the notification title text. Defaults to system font if nil. | Default system font | | messagePushFont | UIFont? | Font of the notification message text. Defaults to system font if nil. | Default system font | Important * If the `useLiquidView` flag is enabled but the user’s system version is lower than **iOS 26**, a regular `UIView`-based push will be shown instead. * If your project is compiled with a Swift version **lower than 5.13**, the Liquid Glass effect will not be available at all — even on iOS 26. In that case, a blurred `UIVisualEffectView` (with `UIBlurEffect`) will be used instead on all devices. **Summary:** * Swift 5.13+ + iOS 26 → Liquid Glass * Swift 5.13+ + iOS < 26 → Standard UIView * Swift < 5.13 → Always blurred view (no Liquid Glass support) **Example Usage:** ```swift Pushwoosh.ForegroundPush.useLiquidView = true Pushwoosh.ForegroundPush.gradientColors = [.red, .orange, .yellow] Pushwoosh.ForegroundPush.titlePushColor = .red Pushwoosh.ForegroundPush.messagePushColor = .green Pushwoosh.ForegroundPush.backgroundColor = .black Pushwoosh.ForegroundPush.titlePushFont = .boldSystemFont(ofSize: 22) Pushwoosh.ForegroundPush.messagePushFont = .italicSystemFont(ofSize: 15) Pushwoosh.ForegroundPush.usePushAnimation = false ``` ### 6. Example of Foreground Push This example demonstrates how to display a custom foreground push notification with `title`, `message`, `cards`, and `GIF animation`. [](https://docs.pushwoosh.com/ios-foreground-custom-5.webm "Example") Pushwoosh foreground push with animated Liquid Glass view [](https://docs.pushwoosh.com/ios-foreground-custom-1.webm "Example") Pushwoosh foreground push with gif attachment [](https://docs.pushwoosh.com/ios-foreground-custom-2.webm "Example") Pushwoosh foreground push with card image [](https://docs.pushwoosh.com/ios-foreground-custom-3.webm "Example") Pushwoosh foreground push with a custom gradient, and custom title and message colors [](https://docs.pushwoosh.com/ios-foreground-custom-4.webm "Example") Pushwoosh foreground push with custom background, title and message fonts, and no animation That’s it. You have successfully configured custom foreground push notifications in iOS with Pushwoosh. --- # iOS SDK FAQ **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-faq/ How to solve common issues with Pushwoosh iOS SDK --- ### iOS integration FAQ #### Q: How can I install the Pushwoosh SDK into my iOS project? You can integrate the Pushwoosh SDK using either Swift Package Manager or CocoaPods. #### Q: How do I install the Pushwoosh SDK using Swift Package Manager? In Xcode, navigate to the **Package Dependencies** section and add the package using the following URL: `https://github.com/Pushwoosh/Pushwoosh-XCFramework`. #### Q: How do I install the Pushwoosh SDK using CocoaPods? Open your project’s `Podfile` and add the line `pod 'PushwooshXCFramework'`. Then, run the command `pod install` in the terminal. #### Q: What capabilities do I need to add to my project to enable Push Notifications? In the Signing & Capabilities section of your project settings, you need to add the `Push Notifications` and `Background Modes` capabilities. When adding `Background Modes`, ensure you check the box for `Remote notifications`. #### Q: What additional capability is required for Time Sensitive Notifications? If you intend to use Time Sensitive Notifications (iOS 15+), you also need to add the `Time Sensitive Notifications` capability. #### Q: Where do I need to add the Pushwoosh initialization code? The initialization code needs to be added to your `AppDelegate` class. #### Q: What initialization code should I add in SwiftUI’s AppDelegate? Please refer to the [provided Swift code example for integrating Pushwoosh in a SwiftUI project](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#appdelegate). #### Q: What initialization code should I add in a standard Swift AppDelegate? Please refer to the [provided Swift code example for integrating Pushwoosh in a standard Swift project](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#appdelegate). #### Q: What initialization code should I add in an Objective-C AppDelegate? Please refer to the [provided Objective-C code example for integrating Pushwoosh in an Objective-C project](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#appdelegate). #### Q: What keys do I need to add to my Info.plist file? You need to add the `Pushwoosh_APPID` key set to your Application Code and the `PW_API_TOKEN` key set to your API Token in your app’s `Info.plist` file. #### Q: What is the purpose of message delivery tracking? Pushwoosh supports tracking delivery events for push notifications using the Notification Service Extension. #### Q: How do I add a Notification Service Extension to my project? In Xcode, go to **File** > **New** > **Target…**, choose **Notification Service Extension**, and press **Next**. Enter a target name and press **Finish**. When prompted to activate, press **Cancel**. #### Q: How do I add the Pushwoosh SDK to the Notification Service Extension? Add the [provided Swift or Objective-C code snippet](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/#add-pushwoosh-sdk-to-notification-service-extension) to your Notification Service Extension’s main `.swift` or `.m` file. This code utilizes `PWNotificationExtensionManager` to handle the notification request. #### Q: What is the purpose of App Groups? App Groups capability is required to allow the Notification Service Extension to communicate with the main application. #### Q: How do I enable App Groups for my main target and Notification Service Extension? For both targets, go to **Signing & Capabilities**, add the **App Groups** capability, and create a new group by pressing the + button. Ensure the group name starts with `group.` and use the same group name for both targets. #### Q: What keys do I need to add to my main target’s Info.plist for App Groups? In your main target’s `Info.plist`, add the `PW_APP_GROUPS_NAME` key and set its value to the name of your App Groups group. #### Q: What keys do I need to add to my Notification Service Extension’s Info.plist? In your Notification Service Extension’s `Info.plist`, add the `PW_APP_GROUPS_NAME` key (set to your App Groups name) and the `Pushwoosh_APPID` key (set to your Application Code). #### Q: How do I verify the Pushwoosh integration? After building and running your project, go to the Pushwoosh Control Panel and send a push notification to your application. You should see the notification appear on your device. #### Q: What are the two main callbacks for handling push notifications in the Pushwoosh SDK? The two main callbacks are `onMessageReceived` and `onMessageOpened`. #### Q: When is the `onMessageReceived` callback invoked? The `onMessageReceived` method is called when a push notification is received by the application. #### Q: When is the `onMessageOpened` callback invoked? The `onMessageOpened` method is called when the user interacts with and opens a push notification. #### Q: How can I access the payload of a received or opened push notification? The payload of a `PWMessage` object (passed to the `onMessageReceived` and `onMessageOpened` callbacks) can be accessed through its `payload` property. #### Q: How can I set a user ID in Pushwoosh? Use the `setUserId(_:)` method of the `Pushwoosh.sharedInstance()` object, passing the user’s ID as an argument. #### Q: How can I set a user’s email address in Pushwoosh? Use the `setEmail(_:)` method of the `Pushwoosh.sharedInstance()` object, providing the user’s email. #### How can I set additional user information as tags in Pushwoosh? Use the `setTags(_:)` method of the `Pushwoosh.sharedInstance()` object, passing a dictionary where keys are tag names and values are the corresponding tag values. #### How can I track a user event with additional attributes? Use the `postEvent(_:withAttributes:)` method of `PWInAppManager.shared()`, providing the event name (e.g., `"login"`) and a dictionary of attributes (e.g., `"name"`, `"last_login"`). #### Q: How can I track a user purchase event with product details? Use the `postEvent(_:withAttributes:)` method of `PWInAppManager.shared()` with the event name `"purchase"` and a dictionary containing product details like `"product_id"`, `"product_name"`, `"price"`, and `"quantity"`. #### Q: How do I set up a delegate to handle rich media presentation? In your view controller or another appropriate class, conform to the `PWRichMediaPresentingDelegate` protocol and set the `delegate` property of `PWRichMediaManager.shared()` to your conforming instance. #### Q: How can I configure the presentation style of rich media? Use the `PWModalWindowConfiguration.shared()` object to configure properties like the modal window position and present/dismiss animations using the `configureModalWindow(with:present:dismiss:)` method. #### Q: What information can I access in the rich media delegate methods? The delegate methods provide access to the `PWRichMedia` object, which contains information about the rich media content, including its `pushPayload`. The `presentingDidFailForRichMedia:withError:` method also provides an `Error` object describing the failure. ### Troubleshooting Caution For load balancing purposes we may need to change the API endpoints in your application. This happens automatically and is handled by our SDK. **Please make sure that nothing in your app is blocking access to any URL containing *.pushwoosh.com.** Note #### If you are behind a Firewall or your WiFi router acts as a Firewall If your device is connected to the Internet via WiFi, and messages don’t get through to the device, please **make sure APNs ports are not blocked by your firewall.**\ Push providers, iOS devices, and Mac computers are often behind firewalls. To send notifications, you will need to allow inbound and outbound TCP packets over port 2195. Devices and computers connecting to push service over **Wi-Fi will need to allow inbound and outbound TCP packets over port 5223.**\ The IP address range for the push service can change; providers are expected to connect by hostname rather than IP address. Push service uses a load balancing scheme that yields a different IP address for the same hostname. However, the entire 17.0.0.0/8 address block is assigned to Apple, so you can specify that range in your firewall rules. #### Q: “Your provisioning profile does not have APS entry” error If you see the “Your provisioning profile does not have APS entry.” error message, make sure your provisioning profile is push compatible and has the `aps-environment` string in it with the `production` or `development` value. The easiest way to check is to open the mobile provisioning profile in any text editor and look for the `aps-environment` string. It should be there. If it is, and you keep receiving the error, it means that XCode still picks a “non push-compatible” profile. Please make sure you sign with the right one - sometimes if you have two profiles for the same App ID XCode messes things up and signs with the old one. The recommended steps are: 1. [Remove](https://stackoverflow.com/questions/18923095/delete-provisioning-profile-from-xcode-5) all XCode profiles; 2. Add the one and only new profile; 3. Rebuild the app. #### Q: What does iOS push token and hwid look like? iOS device push tokens are strings with 64 hexadecimal symbols. Push token example:\ `03df25c845d460bcdad7802d2vf6fc1dfde97283bf75cc993eb6dca835ea2e2f`\ Make sure that iOS push tokens you use when targeting specific devices in your API requests are in **lower case**. Pushwoosh uses IDFV as the device HWID. Note that it may change in case user re-installs all apps from a specific vendor. `CBAF8ED1-17FB-49A3-73BD-DC79B63AEF93` #### Q: How do I switch banner on and off when my app is running? By default our latest iOS SDK displays the notification banner when the app is running in the foreground.\ You can control this behavior by changing the following flags in the Info.plist: Flag `Pushwoosh_ALERT_TYPE` – string type, values are: * `BANNER` – default value, displays banner in-app alert * `ALERT` – alert notification * `NONE` – do not show notifications when the app is in the foreground #### Q: How do I obtain my iOS device push token to use in Test Devices? To get your iOS device push token, 1. Open Xcode => Devices & Simulators; 2. Connect the test device to your computer; 3. Select your connected device in the list of devices on the left; 4. Click the Open Console button. Launch the application for which you need the device push token, and locate your 64 hexadecimal characters device push token in the “Registered for push notifications” line: #### Q: Why are badges not cleared after I uninstall my app and re-install it in a short period? It is an expected behavior - badges are handled by iOS itself and its values are stored in application preferences, which are not instantly removed when you uninstall your app. A badge value remains there for some time in case of immediate re-installation, resulting in the same badges shown after an app is re-installed. --- # gRPC Transport **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-grpc/ Optional gRPC transport layer for improved network performance --- The `PushwooshGRPC` module provides an optional gRPC transport layer that can improve network performance for communication with Pushwoosh servers. When available, the SDK automatically uses gRPC for supported API methods and falls back to REST if gRPC is unavailable. Note Available starting from **SDK version 7.0.18**. ## How it works By default, the Pushwoosh SDK communicates with servers using REST API over HTTPS. The `PushwooshGRPC` module adds an alternative gRPC transport that offers several advantages: * **Lower latency** — gRPC uses HTTP/2 with persistent connections * **Smaller payload size** — Protocol Buffers are more compact than JSON * **Multiplexing** — Multiple requests can be sent over a single connection ### Automatic fallback The module includes built-in resilience: | Scenario | Behavior | | ---------------- | ------------------- | | gRPC available | Uses gRPC transport | | gRPC unavailable | Falls back to REST | | Network error | Retries with REST | Tip You don’t need to handle transport selection in your code. The SDK automatically chooses the best available transport. ## Requirements | Requirement | Version | | ----------- | ------- | | iOS | 13.0+ | | Xcode | 14.0+ | | Swift | 5.0+ | ## Installation ### Swift Package Manager Add `PushwooshGRPC` to your target when integrating the Pushwoosh SDK: 1. In Xcode, go to **File → Add Package Dependencies** 2. Enter the package URL: `https://github.com/Pushwoosh/Pushwoosh-XCFramework` 3. Select `PushwooshGRPC` in addition to the required frameworks * Required frameworks * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` * Optional frameworks * `PushwooshGRPC` — gRPC transport * `PushwooshKeychain` — Persistent device ID * `PushwooshLiveActivities` — Live Activities support * `PushwooshVoIP` — VoIP push notifications * `PushwooshForegroundPush` — Custom foreground notifications ### CocoaPods Add the gRPC subspec to your `Podfile`: ```ruby target 'MyApp' do use_frameworks! pod 'PushwooshXCFramework' pod 'PushwooshXCFramework/PushwooshGRPC' end ``` Then run: ```bash pod install ``` ## Usage **No code changes required.** Once you add the `PushwooshGRPC` module to your project, it works automatically: 1. On app launch, the SDK detects that gRPC transport is available 2. API calls are routed through gRPC when possible 3. If gRPC fails, the SDK automatically falls back to REST ## Supported methods The following API methods support gRPC transport: | Method | gRPC Support | | ------------------- | ------------ | | Device registration | Yes | | Tags (set/get) | Yes | | App open tracking | Yes | | Push statistics | Yes | Note Methods not listed above will continue to use REST transport. The SDK handles this transparently. ## Use cases The `PushwooshGRPC` module is particularly useful for: * **High-frequency event tracking** — Reduced overhead for apps that send many events * **Real-time applications** — Lower latency for time-sensitive operations * **Bandwidth-constrained environments** — Smaller payload sizes save data ## Troubleshooting ### Verifying the module is active Check the Xcode console logs when your app launches. You should see a log message like: ```plaintext [Pushwoosh] gRPC transport: ENABLED ``` ### Forcing REST transport If you need to disable gRPC temporarily for debugging, you can remove the `PushwooshGRPC` module from your target. The SDK will automatically use REST transport when gRPC is not available. Caution Do not include both gRPC and REST transport code in your app — the SDK handles transport selection automatically based on module availability. --- # iOS image carousel push notifications **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-image-carousel-push-notifications/ Image carousel in Pushwoosh iOS push notifications using Swift. --- The `UNNotificationContentExtension` protocol provides the entry point for a notification content app extension, which displays a custom interface for your app’s notifications. [](https://docs.pushwoosh.com/ios-push-notifications-ios-image-carousel-push-notifications-1.webm) ## 1. Add Notification Content Extension In Xcode, select File > New > Target… Choose the Notification Content Extension Name it **NotificationContentExtension** ## 2. Add code to your app [Download PWNotificationExtension from Github](https://github.com/Pushwoosh/PWNotificationContentExtension) and replace the **`PWNotificationContentExtension`** in your Xcode Project with the same file from Github. ## 3. Notification category Add the code bellow to your **AppDelegate.swift** file ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { if #available(iOS 10.0, *) { let options: UNAuthorizationOptions = [.alert] UNUserNotificationCenter.current().requestAuthorization(options: options) { (authorized, error) in if authorized { let categoryIdentifier = "PWNotificationCarousel" let carouselNext = UNNotificationAction(identifier: "PWNotificationCarousel.next", title: "➡️ RIGHT", options: []) let carouselPrevious = UNNotificationAction(identifier: "PWNotificationCarousel.previous", title: "⬅️ LEFT", options: []) let carouselCategory = UNNotificationCategory(identifier: categoryIdentifier, actions: [carouselNext, carouselPrevious], intentIdentifiers: [], options: []) UNUserNotificationCenter.current().setNotificationCategories([carouselCategory]) } } } return true } ``` ## 4. Send a push notification **iOS Category:** Send a push notification with APS json: ![APS JSON](https://docs.pushwoosh.com/ios-push-notifications-ios-image-carousel-push-notifications-6.webp) ```json { "aps":{ "category":"PWNotificationCarousel" } } ``` **Custom Data** You must list the Image URLs separated by a comma `,` ![Example](https://docs.pushwoosh.com/ios-push-notifications-ios-image-carousel-push-notifications-7.webp) ```json { "images":"image1.jpg, image2.jpg, image3.jpg" } ``` Once you receive the push, you will need to long press or swipe left and click “View” to expand the notification depending on the iOS version. --- # Persistent Device ID (Keychain) **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-keychain/ Preserve device identification across app reinstalls using iOS Keychain --- The `PushwooshKeychain` module provides persistent device identification (HWID) that survives app reinstallation. This is useful for testing and development scenarios where you need to maintain the same device identity even after reinstalling the app. Note Available starting from **SDK version 7.0.16**. ## How it works By default, iOS generates a new `identifierForVendor` (IDFV) each time an app is reinstalled, which results in a new HWID being registered with Pushwoosh. The `PushwooshKeychain` module stores the HWID in the iOS Keychain, which persists across app reinstalls. ### Environment detection The module automatically detects the app environment and behaves differently: | Environment | Persistent HWID | | ----------------- | --------------- | | Simulator | Enabled | | Debug/Development | Enabled | | TestFlight | Enabled | | App Store | **Disabled** | Important The module is **automatically disabled** in App Store builds for privacy compliance. This ensures that production users always get a fresh HWID when they reinstall your app. If you prefer not to include this module in your production app at all, you can safely remove `PushwooshKeychain` from your release build configuration or use conditional linking for Debug/TestFlight builds only. ## Installation ### Swift Package Manager Add `PushwooshKeychain` to your target when integrating the Pushwoosh SDK: 1. In Xcode, go to **File → Add Package Dependencies** 2. Enter the package URL: `https://github.com/Pushwoosh/Pushwoosh-XCFramework` 3. Select `PushwooshKeychain` in addition to the required frameworks * Required frameworks * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` * Optional frameworks * `PushwooshKeychain` — Persistent device ID * `PushwooshLiveActivities` — Live Activities support * `PushwooshVoIP` — VoIP push notifications * `PushwooshForegroundPush` — Custom foreground notifications ### CocoaPods Add the Keychain subspec to your `Podfile`: ```ruby target 'MyApp' do use_frameworks! pod 'PushwooshXCFramework' pod 'PushwooshXCFramework/PushwooshKeychain' end ``` Then run: ```bash pod install ``` ## Usage **No code changes required.** Once you add the `PushwooshKeychain` module to your project, it works automatically: 1. On first app launch, the module generates an HWID and stores it in the Keychain 2. On subsequent launches (including after reinstall), the module retrieves the stored HWID 3. The SDK uses this persistent HWID for device registration with Pushwoosh ## Use cases The `PushwooshKeychain` module is particularly useful for: * **QA testing** — Maintain the same device identity across multiple app installs during testing * **Development** — Keep consistent device targeting while iterating on your app * **TestFlight beta testing** — Track the same beta testers across app updates and reinstalls Tip Since the module is disabled in App Store builds, you don’t need to conditionally include it — it’s safe to ship with your production app. ## Troubleshooting ### Verifying the module is active Check the Xcode console logs when your app launches. You should see a log message like: ```plaintext [Pushwoosh] Detected environment: Debug. Persistent HWID: ENABLED ``` or ```plaintext [Pushwoosh] Detected environment: App Store. Persistent HWID: DISABLED ``` ### Clearing the stored HWID If you need to reset the persistent HWID during development, you can call: * Swift ```swift import PushwooshFramework // Clear the stored HWID from Keychain Pushwoosh.Keychain.clearPersistentHWID() ``` * Objective-C ```objective-c @import PushwooshFramework; // Clear the stored HWID from Keychain [Pushwoosh.Keychain clearPersistentHWID]; ``` Note After clearing, the next app launch will generate and store a new HWID. --- # iOS Live Activities **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/ Learn how to enable Live Activities in your iOS app using the Pushwoosh iOS SDK. --- Tip Watch a video about iOS Live Activities [Youtube Video: iOS Live Activities](https://youtube.com/watch?v=jRrDh_pIZCE) [Live Activities](https://developer.apple.com/design/human-interface-guidelines/live-activities) display your app’s most current data on the iPhone or iPad Lock Screen and in the Dynamic Island. This feature allows users to see live information at a glance and perform quick actions related to the displayed information. Here are a few examples of using Live Activities: * Show order status in a delivery app; * Provide a real-time countdown in a training app; * Show tracking information in a taxi app; * Display game stats and current scores in a sports app; * Provide hourly forecasts in a weather app. You can enable Live Activities using the Pushwoosh iOS SDK as described below. To manage Live Activities and update their content, use the [/updateLiveActivity](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api#updateliveactivity) method. ## Setup Important Live Activities in Pushwoosh only support [token-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/). [Certificate-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration/) is not supported. ### Add a Widget Extension 1. Create a new target Go to **File > New > Target** and select **Widget Extension**. 2. Widget Extension Configuration Please enter a name and make sure to select **Include Live Activity** and click **Finish**. ### Info.plist configuration Find the Info.plist file in the primary target, insert the “Supports Live Activities” key, and set its value to YES. ```xml NSSupportsLiveActivities ``` Frequent updates If your Live Activity is updated often (for example, live sports scores), also add `NSSupportsLiveActivitiesFrequentUpdates` set to `true`. Apple limits how many high-priority push updates an app can send per hour. This key raises that budget. See the `apns_priority` parameter in the [Live Activities API reference](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/#time-sensitive-push-and-delivery-priority) for how delivery priority is controlled per update. ```xml NSSupportsLiveActivitiesFrequentUpdates ``` ### Enabling live activities from app To enable Live Activities, add their code to your existing widget extension or create a new one if your app doesn’t already have it. Live Activities use [SwiftUI](https://developer.apple.com/documentation/SwiftUI) and [WidgetKit](https://developer.apple.com/documentation/WidgetKit) functionality for their user interface. ActivityKit handles the life cycle of each Live Activity: its API is used to request, update, and end a Live Activity and to receive ActivityKit push notifications. You can learn more about Live Activities in the [Apple documentation](https://developer.apple.com/documentation/activitykit/displaying-live-data-with-live-activities). 1. Navigate to the ContentView file of your project in Xcode and create a Button ```swift import SwiftUI struct ContentView: View { var body: some View { VStack(spacing: 20) { Button(action: { LiveActivityManager.shared.startActivity() }, label: { Text("Start Live Activity") .foregroundColor(.white) .padding() .background(Color.blue) .cornerRadius(10) }) } .padding() } } #Preview { ContentView() } ``` 2. Create a LiveActivityManager.swift file to manage Live Activities ```swift import Foundation import ActivityKit import UIKit import PushwooshFramework import PushwooshLiveActivities class LiveActivityManager: NSObject, ObservableObject { public static let shared: LiveActivityManager = LiveActivityManager() private var currentActivity: Activity? = nil override init() { super.init() } func startActivity() { guard ActivityAuthorizationInfo().areActivitiesEnabled else { print("You can't start live activity.") return } do { let pushwooshData = PushwooshLiveActivityAttributeData(activityId: "activity_id") let atttribute = FoodDeliveryAttributes(orderNumber: "1234567", pushwoosh: pushwooshData) let initialState = FoodDeliveryAttributes.ContentState( status: "Preparing your meal", estimatedTime: "25 min", emoji: "👨‍🍳", pushwoosh: nil ) let activity = try Activity.request( attributes: atttribute, content: .init(state:initialState , staleDate: nil), pushType: .token ) self.currentActivity = activity Task { for await pushToken in activity.pushTokenUpdates { let pushTokenString = pushToken.reduce("") { $0 + String(format: "%02x", $1) } print("Activity:\(activity.id) push token: \(pushTokenString)") // MARK: - Send Push Token to Pushwoosh Pushwoosh.LiveActivities.startLiveActivity( token: pushTokenString, activityId: "activity_id" ) } } } catch { print("Start Activity Error: \(error.localizedDescription)") } } } ``` 3. That’s it, now we run the project and press the ‘Start Live Activity’ button. Then we navigate to the lock screen and see the created Live Activity. ### Start Live Activity with a Remote push notification 1. To initiate a Live Activity via Remote Push Notification, you need to send the pushToStartTokenUpdates token to Pushwoosh. ```swift func getPushToStartToken() { if #available(iOS 17.2, *) { Task { for await data in Activity.pushToStartTokenUpdates { let token = data.map {String(format: "%02x", $0)}.joined() print("Activity PushToStart Token: \(token)") // Send `pushToStartTokenUpdates` token to Pushwoosh try await Pushwoosh.LiveActivities.sendPushToStartLiveActivity(token: token) } } } } ``` 2. Initiate a Live Activity with a Remote Push Notification Tip Follow our [Pushwoosh API reference](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api#startliveactivity) for instructions and examples on making a request to start a Live Activity remotely. ### Managing Live Activities The Pushwoosh iOS SDK provides the following methods for working with Live Activities: ```swift // Send Live Activity Push To Start Token to Pushwoosh static func sendPushToStartLiveActivity(token: String) static func sendPushToStartLiveActivity(token: String, completion: @escaping (Error?) -> Void) // Start Live Activity Methods with Activity ID static func startLiveActivity(token: String, activityId: String) static func startLiveActivity(token: String, activityId: String, completion: @escaping (Error?) -> Void) // Stop Live Activity Methods static func stopLiveActivity() static func stopLiveActivity(completion: @escaping (Error?) -> Void) static func stopLiveActivity(activityId: String) static func stopLiveActivity(activityId: String, completion: @escaping (Error?) -> Void) // Schedule a Live Activity to start at a future date (iOS 26.0+) static func schedule(attributes: Attributes, contentState: Attributes.ContentState, at startDate: Date, alertTitle: String, alertBody: String) throws -> Activity // Cancel a scheduled or running Live Activity by its Activity ID (iOS 16.2+) static func cancel(_ activityType: Attributes.Type, activityId: String) ``` You can also update live activities by segments using the Activity ID parameter. When creating an activity, you need to pass a unique Activity ID parameter in the method, which will be relevant for a specific user segment. For example, N users have subscribed to the same event in a live Activity. It is necessary that the Activity ID parameter be unique for all of these N users. When you finish working with a Live Activity, use these methods: ```swift static func stopLiveActivity() static func stopLiveActivity(completion: @escaping (Error?) -> Void) ``` Note You can manage iOS Live Activities via [Pushwoosh API](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api). ### Scheduling a Live Activity to start at a future date Note Requires iOS 26.0+. Instead of starting a Live Activity immediately, you can schedule it to start at a future date. `alertTitle` and `alertBody` are shown to the user in the local notification alert fired when the scheduled activity actually starts: ```swift if #available(iOS 26.0, *) { let startDate = Date().addingTimeInterval(3600) // starts in 1 hour do { let activity = try Pushwoosh.LiveActivities.schedule( attributes: atttribute, contentState: initialState, at: startDate, alertTitle: "Game starting!", alertBody: "The match is about to begin" ) self.currentActivity = activity } catch { print("Schedule Activity Error: \(error.localizedDescription)") } } ``` `startDate` must be in the future, otherwise the call throws. Call `schedule` on the main thread while the app is in the foreground. There is no schedule-time request to Pushwoosh: the server learns about the activity once it actually starts and receives its push token through the same token observer installed by the `setup()` method (see below). ### Cancelling a Live Activity by Activity ID Note Requires iOS 16.2+. Use `cancel(_:activityId:)` to cancel a Live Activity by its Activity ID without holding a reference to the `Activity` instance: ```swift if #available(iOS 16.2, *) { Pushwoosh.LiveActivities.cancel(FoodDeliveryAttributes.self, activityId: "activity_id") } ``` `cancel` ends the activity on the device right away and notifies the Pushwoosh server. This differs from `stopLiveActivity(activityId:)`, which only notifies the server and does not end the activity on the device directly. `cancel` also works for a Live Activity that was scheduled with `schedule` but hasn’t started yet — it gets cancelled before it ever starts. ### `Setup()` method. Pushwoosh simplifies the transfer of activity IDs by introducing the `PushwooshLiveActivities.setup` function, which handles the entire lifecycle of a Live Activity within the application. This function automatically listens for both pushToStart and pushToUpdate token updates. By using this method, the application no longer needs to manually track the initiation of Live Activities or manage token updates for activity updates. We recommend using this method because it handles all token management on our side, reducing the amount of code you need to maintain on your end. This simplifies the integration and ensures a smoother, more efficient experience for your app. In the AppDelegate, ensure you import `PushwooshFramework` and `PushwooshLiveActivities` call the `setup` method from the `Pushwoosh.LiveActivities` module. **AppDelegate.swift** ```swift if #available(iOS 16.1, *) { Pushwoosh.LiveActivities.setup(FoodDeliveryAttributes.self) } ``` **FoodDeliveryAttributes** ```swift import WidgetKit import SwiftUI import ActivityKit import PushwooshFramework import PushwooshLiveActivities struct FoodDeliveryAttributes: PushwooshLiveActivityAttributes { public struct ContentState: PushwooshLiveActivityContentState { var status: String var estimatedTime: String var emoji: String var pushwoosh: PushwooshLiveActivityContentStateData? } var orderNumber: String var pushwoosh: PushwooshLiveActivityAttributeData } ``` `FoodDeliveryAttributes`: This structure conforms to the `PushwooshLiveActivityAttributes` protocol. It is used to define the attributes of a live activity within the app. Note You can manage Live Activities and update their content using the /updateLiveActivity method of the Pushwoosh API. For more information, please read [this guide](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api) ## Migration guide Starting from version 6.8.0 of the Pushwoosh iOS SDK, we’ve updated the SDK structure. Live Activities methods are now accessed through the `PushwooshLiveActivities` module. If you were using a version of the Pushwoosh iOS SDK earlier than 6.8.0 and were calling the methods listed below, and have since updated to version 6.8.0 or later, please note the following changes: ```swift static func setup(_ activityType: Attributes.Type) static func defaultSetup() static func defaultStart(_ activityId: String, attributes: [String: Any], content: [String: Any]) ``` Now, to access these methods, you should use the LiveActivity module. ```swift import PushwooshFramework import PushwooshLiveActivities ``` ```swift Pushwoosh.LiveActivities.setup(FoodDeliveryAttributes.self) Pushwoosh.LiveActivities.defaultSetup() Pushwoosh.LiveActivities.defaultStart("activity_id", attributes: ["key_attribute": "value_attribute"], content: ["key_content": "value_content"]) ``` We have also maintained support for the methods through `Pushwoosh.sharedInstance()` as listed below, but please note that these methods will be deprecated in future releases. ```swift // Send Live Activity Push To Start Token to Pushwoosh static func sendPushToStartLiveActivity(token: String) static func sendPushToStartLiveActivity(token: String, completion: @escaping (Error?) -> Void) // Start Live Activity Methods with Activity ID static func startLiveActivity(token: String, activityId: String) static func startLiveActivity(token: String, activityId: String, completion: @escaping (Error?) -> Void) // Stop Live Activity Methods static func stopLiveActivity() static func stopLiveActivity(completion: @escaping (Error?) -> Void) static func stopLiveActivity(activityId: String) static func stopLiveActivity(activityId: String, completion: @escaping (Error?) -> Void) ``` --- # iOS message delivery tracking **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-message-delivery-tracking/ How to add the Notification Service Extension to your iOS native app to track message delivery --- There is an [API method](https://docs.pushwoosh.com/developer/api-reference/device-api#messagedeliveryevent) in Pushwoosh that tracks the delivery of push notifications. iOS apps do not support this method out of the box, because push notifications on iOS are handled by the OS, not by the Pushwoosh SDK. You can add delivery tracking by adding a Notification Service Extension to your project. This page shows how to implement message delivery tracking for iOS apps. Note Requires Pushwoosh iOS SDK 7.x, which supports iOS 13.0 and later. Note Since Pushwoosh iOS SDK 7.1.0 the recommended integration is the drop-in `PushwooshNotificationServiceExtension` base class shown below. It sends the message delivery event, sets the badge, downloads the media attachment, and handles the mandatory `serviceExtensionTimeWillExpire` fallback for you. The older `PWNotificationExtensionManager` API still works but is deprecated — see [Legacy integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-message-delivery-tracking/#legacy-integration). ## Add Notification Service Extension 1. In Xcode, select **File** > **New** > **Target…** 2. Select **Notification Service Extension** and press **Next.** ![Xcode target template picker with Notification Service Extension selected](https://docs.pushwoosh.com/ios-push-notifications-ios-message-delivery-tracking-1.webp) 3. Enter the product name and press **Finish.** Caution Do not select **Activate** on the dialog that is shown after pressing **Finish**. 4. Press **Cancel** on the **Activate scheme** prompt. ![Activate scheme prompt with Cancel highlighted](https://docs.pushwoosh.com/ios-push-notifications-ios-message-delivery-tracking-2.webp) By canceling, you keep Xcode debugging your app instead of the extension you just created. If you activated it by accident, you can switch back to debugging your app within Xcode. ## Dependencies for the Notification Service Extension (CocoaPods only) If you use Swift Package Manager to manage dependencies, you can skip this step, as the dependencies are added automatically. Open your `Podfile` and add the dependency for the target: Podfile ```ruby target 'NotificationServiceExtension' do use_frameworks! pod 'PushwooshXCFramework' end ``` Run the following commands in the terminal to install the dependencies: ```shell rm -rf Podfile.lock pod deintegrate pod setup pod repo update pod install ``` ## Add code for tracking message delivery events Make your extension a subclass of `PushwooshNotificationServiceExtension`. An empty subclass is enough: Pushwoosh sends the message delivery event, sets the badge, downloads the media attachment, and handles the timeout fallback automatically. Replace the generated contents of your **NotificationService** file: * Swift ```swift import UserNotifications import PushwooshFramework class NotificationService: PushwooshNotificationServiceExtension {} ``` * Objective-C ```objective-c #import @interface NotificationService : PushwooshNotificationServiceExtension @end @implementation NotificationService @end ``` Tip If you do not need any custom code, you can skip the source file entirely and point the extension’s Info.plist `NSExtensionPrincipalClass` at `PushwooshNotificationServiceExtension` directly. ### App ID Since 7.1.0 the extension inherits `Pushwoosh_APPID` (and other `Pushwoosh_*` keys) from the host app’s Info.plist, so you no longer need to duplicate it in the extension. Add `Pushwoosh_APPID` to the extension Info.plist only when you want to override the host value: NotificationService/Info.plist ```xml Pushwoosh_APPID XXXXX-XXXXX ``` Note On Pushwoosh iOS SDK versions earlier than 7.1.0 the extension does not inherit configuration from the host app. On those versions you must add `Pushwoosh_APPID` to the extension Info.plist. ### App Group (badge and reverse proxy) An App Group shared between the app and the extension is needed to sync the badge count and to read reverse-proxy settings that the host app stores. 1. Add the **App Groups** capability to the extension target and enable the same group there as in the host app. This is required — without the shared container the badge count and reverse-proxy settings cannot be synced. 2. Provide the App Group name. Like `Pushwoosh_APPID`, the extension inherits `PW_APP_GROUPS_NAME` from the host app’s Info.plist since 7.1.0, so if you already set it there for badges you do not need to add it to the extension. Set it in the extension Info.plist only to override the host value, or provide it programmatically by overriding `pushwooshAppGroupsName`. App Info.plist ```xml PW_APP_GROUPS_NAME group.com.example.app ``` Caution If the host app uses a reverse proxy (`Pushwoosh_ALLOW_REVERSE_PROXY`), the extension needs this App Group to read the proxy URL the app stored there. Without it, the delivery event is held back rather than sent directly, bypassing the proxy. ## Customize the notification (optional) The base class exposes a few override points, from least to most control. Pushwoosh still runs the delivery event, badge, attachment, and timeout fallback in every case. Set the App Group programmatically instead of using the Info.plist key: ```swift override func pushwooshAppGroupsName() -> String? { "group.com.example.app" } ``` Run asynchronous preparation before Pushwoosh processes the push — for example, prefetching Push Stories media — without overriding the standard `didReceive`. Call `completion` exactly once, on the main thread: ```swift override func pushwooshPrepare(for request: UNNotificationRequest, completion: @escaping () -> Void) { // async work here completion() } ``` Modify the content before it is shown by overriding `didReceive`. Call `super` with your own content handler, mutate the content inside it, then forward it to the original handler: ```swift override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { super.didReceive(request) { content in let mutable = (content.mutableCopy() as? UNMutableNotificationContent) ?? content // customize `mutable` here contentHandler(mutable) } } ``` ## Legacy integration Caution `PWNotificationExtensionManager` is deprecated since 7.1.0. Use it only if you cannot subclass `PushwooshNotificationServiceExtension` — for example, an extension that already extends another SDK’s base class, or a cross-platform wrapper (React Native, Flutter, Unity). New integrations should use the base class above. This low-level API drives the same processing (delivery event, badge, attachment) from a plain `UNNotificationServiceExtension`: * Swift ```swift import UserNotifications import PushwooshFramework class NotificationService: UNNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { PWNotificationExtensionManager.sharedManager() .handleNotificationRequest(request, contentHandler: contentHandler) } } ``` * Objective-C ```objective-c #import "PWNotificationExtensionManager.h" @interface NotificationService : UNNotificationServiceExtension @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent *))contentHandler { [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } @end ``` ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # iOS modal rich media **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-modal-rich-media/ Send Modal Rich Media to iOS users --- Starting from Pushwoosh SDK version 6.7.5, you have the ability to send **Modal Rich Media**. We are introducing new **Modal Rich Media**, which can be customized. The new Modal Rich Media do not completely block the screen and can be positioned in different parts of the screen (top, bottom, and center). > For more information on Rich Media pages, please refer to [our guide](https://docs.pushwoosh.com/product/content/rich-media). ## Configuration Note By default, the Pushwoosh iOS SDK supports displaying legacy rich media. Note If you plan to use modal rich media display as in-app messages in the Customer Journey Builder, make sure you have implemented the code shown below. See the [basic SDK integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/) for details. ```swift //for silent push notifications func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.configure.handlePushReceived(userInfo) completionHandler(.noData) } ``` ### Option 1: Info.plist configuration To enable the display of Modal Rich Media, set the parameter `Pushwoosh_RICH_MEDIA_STYLE` in your **info.plist** and assign it the value `MODAL_RICH_MEDIA`. **Info.plist**: ```xml Pushwoosh_RICH_MEDIA_STYLE MODAL_RICH_MEDIA ``` ### Option 2: Programmatic configuration (SDK 7.0.14+) Tip Available since Pushwoosh iOS SDK version **7.0.14**. You can also configure the Rich Media presentation style programmatically in your AppDelegate: * Swift ```swift func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Set modal presentation style Pushwoosh.media.setRichMediaPresentationStyle(.modal) // Configure modal window appearance Pushwoosh.media.modalRichMedia.configure( position: .PWModalWindowPositionBottom, presentAnimation: .PWAnimationPresentFromBottom, dismissAnimation: .PWAnimationDismissDown ) Pushwoosh.configure.registerForPushNotifications() return true } ``` * Objective-C ```objective-c - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { // Set modal presentation style [[Pushwoosh media] setRichMediaPresentationStyle:PWRichMediaPresentationStyleModal]; // Configure modal window appearance [[[Pushwoosh media] modalRichMedia] configureWithPosition:PWModalWindowPositionBottom presentAnimation:PWAnimationPresentFromBottom dismissAnimation:PWAnimationDismissDown]; [[Pushwoosh configure] registerForPushNotifications]; return YES; } ``` By default, Modal Rich Media will be displayed in the center of the screen with a bottom-to-top appearance animation. [](https://docs.pushwoosh.com/ios-push-notifications-ios-modal-rich-media-1.webm "Example") ### Modal Rich Media positioning Modal Rich Media can be positioned in different locations on the screen. ```objective-c /** Enum defining the possible positions for displaying a modal window. - `PWModalWindowPositionTop`: The modal window appears at the top of the screen, within the safe area. - `PWModalWindowPositionCenter`: The modal window appears at the center of the screen, within the safe area. - `PWModalWindowPositionBottom`: The modal window appears at the bottom of the screen, within the safe area. - `PWModalWindowPositionBottomSheet`: The modal window appears at the very bottom of the screen, ignoring the safe area. - `PWModalWindowPositionDefault`: The default position is the center of the screen, within the safe area. */ typedef NS_ENUM(NSInteger, ModalWindowPosition) { PWModalWindowPositionTop, // Appears at the top of the screen (within safe area) PWModalWindowPositionCenter, // Appears at the center of the screen (within safe area) PWModalWindowPositionBottom, // Appears at the bottom of the screen (within safe area) PWModalWindowPositionBottomSheet, // Appears at the very bottom of the screen (ignores safe area) PWModalWindowPositionFullScreen, // Fullscreen, ignores safe area insets PWModalWindowPositionDefault // Default position (center of the screen, within safe area) }; ``` The example below shows a Modal Rich Media displayed at the top of the screen. [](https://docs.pushwoosh.com/ios-push-notifications-ios-modal-rich-media-2.webm "Example") Modal Rich Media show animations include: ```objective-c typedef NS_ENUM(NSInteger, PresentModalWindowAnimation) { PWAnimationPresentFromBottom, PWAnimationPresentFromTop, PWAnimationPresentFromRight, PWAnimationPresentFromLeft, PWAnimationPresentNone }; ``` Modal Rich Media close animations include: ```objective-c typedef NS_ENUM(NSInteger, DismissModalWindowAnimation) { PWAnimationDismissDown, PWAnimationDismissUp, PWAnimationDismissLeft, PWAnimationDismissRight, PWAnimationCurveEaseInOut, PWAnimationDismissNone, /** * Default dismiss animation is `PWAnimationCurveEaseInOut` */ PWAnimationDismissDefault }; ``` The example below demonstrates displaying Modal Rich Media at the bottom of the screen with a left-to-right show animation and a rightward close animation: [](https://docs.pushwoosh.com/ios-push-notifications-ios-modal-rich-media-3.webm "Example") ### Additional parameters for Modal Rich Media Tip The following API is available since Pushwoosh iOS SDK version **7.0.14**. Additional parameters for displaying Modal Rich Media include options like adding haptic feedback of type vibration, enabling swipe gestures, and setting an automatic close timer after a specified duration. * Swift ```swift // Haptic Feedback Type Pushwoosh.media.modalRichMedia.setHapticFeedbackType(.PWHapticFeedbackLight) // Swipe directions to dismiss Pushwoosh.media.modalRichMedia.setDismissSwipeDirections([ NSNumber(value: DismissSwipeDirection.PWSwipeDismissDown.rawValue), NSNumber(value: DismissSwipeDirection.PWSwipeDismissUp.rawValue) ]) // Set Rich Media corner radius let topCorners = PWCornerTypeTopLeft.rawValue | PWCornerTypeTopRight.rawValue Pushwoosh.media.modalRichMedia.setCornerType(CornerType(rawValue: topCorners), withRadius: 16) // Close Modal Rich Media after N seconds Pushwoosh.media.modalRichMedia.closeAfter(3) ``` * Objective-C ```objective-c // Haptic Feedback Type [[[Pushwoosh media] modalRichMedia] setHapticFeedbackType:PWHapticFeedbackLight]; // Swipe directions to dismiss [[[Pushwoosh media] modalRichMedia] setDismissSwipeDirections:@[ @(PWSwipeDismissDown), @(PWSwipeDismissUp) ]]; // Set Rich Media corner radius CornerType topCorners = PWCornerTypeTopLeft | PWCornerTypeTopRight; [[[Pushwoosh media] modalRichMedia] setCornerType:topCorners withRadius:16.0]; // Close Modal Rich Media after N seconds [[[Pushwoosh media] modalRichMedia] closeAfter:3.0]; ``` **Haptic Feedback Types:** ```objective-c typedef NS_ENUM(NSInteger, HapticFeedbackType) { PWHapticFeedbackLight, // Light vibration feedback PWHapticFeedbackMedium, // Medium vibration feedback PWHapticFeedbackHard, // Strong vibration feedback PWHapticFeedbackNone // Vibration is off (default) }; ``` **Swipe Directions:** ```objective-c typedef NS_ENUM(NSInteger, DismissSwipeDirection) { PWSwipeDismissDown, PWSwipeDismissUp, PWSwipeDismissLeft, PWSwipeDismissRight, PWSwipeDismissNone }; ``` ## PWRichMediaPresentingDelegate Tip The delegate API via `Pushwoosh.media.modalRichMedia.delegate` is available since SDK version **7.0.14**. To manage the queue of modal rich media, you need to implement the delegate methods of `PWRichMediaPresentingDelegate`. When using this functionality, modal rich media are presented sequentially, and the next one will not be displayed until the user closes the current one. Once the user closes the presented rich media, the next one, which was part of a different push notification, will be shown. * Swift ```swift import UIKit import PushwooshFramework @main class AppDelegate: UIResponder, UIApplicationDelegate, PWRichMediaPresentingDelegate { var richMediaQueue: [PWRichMedia] = [] var isPresenting = false func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Configure modal rich media Pushwoosh.media.setRichMediaPresentationStyle(.modal) Pushwoosh.media.modalRichMedia.configure( position: .PWModalWindowPositionBottom, presentAnimation: .PWAnimationPresentFromBottom, dismissAnimation: .PWAnimationDismissDown ) // Set delegate Pushwoosh.media.modalRichMedia.delegate = self Pushwoosh.configure.registerForPushNotifications() return true } // MARK: - PWRichMediaPresentingDelegate func richMediaManager(_ richMediaManager: PWRichMediaManager, shouldPresent richMedia: PWRichMedia) -> Bool { if !richMediaQueue.contains(where: { $0 === richMedia }) { richMediaQueue.append(richMedia) } return !isPresenting } func richMediaManager(_ richMediaManager: PWRichMediaManager, didPresent richMedia: PWRichMedia) { isPresenting = true } func richMediaManager(_ richMediaManager: PWRichMediaManager, didClose richMedia: PWRichMedia) { isPresenting = false if let idx = richMediaQueue.firstIndex(where: { $0 === richMedia }) { richMediaQueue.remove(at: idx) } // Present next rich media in queue if let nextRichMedia = richMediaQueue.first { Pushwoosh.media.modalRichMedia.present(nextRichMedia) } } func richMediaManager(_ richMediaManager: PWRichMediaManager, presentingDidFailFor richMedia: PWRichMedia, withError error: Error) { richMediaManager(richMediaManager, didClose: richMedia) } } ``` * Objective-C ```objective-c #import "AppDelegate.h" #import @interface AppDelegate () @property (nonatomic, strong) NSMutableArray *richMediaQueue; @property (nonatomic, assign) BOOL isPresenting; @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.richMediaQueue = [NSMutableArray array]; self.isPresenting = NO; // Configure modal rich media [[Pushwoosh media] setRichMediaPresentationStyle:PWRichMediaPresentationStyleModal]; [[[Pushwoosh media] modalRichMedia] configureWithPosition:PWModalWindowPositionBottom presentAnimation:PWAnimationPresentFromBottom dismissAnimation:PWAnimationDismissDown]; // Set delegate [[[Pushwoosh media] modalRichMedia] setDelegate:self]; [[Pushwoosh configure] registerForPushNotifications]; return YES; } #pragma mark - PWRichMediaPresentingDelegate - (BOOL)richMediaManager:(PWRichMediaManager *)richMediaManager shouldPresentRichMedia:(PWRichMedia *)richMedia { if (![self.richMediaQueue containsObject:richMedia]) { [self.richMediaQueue addObject:richMedia]; } return !self.isPresenting; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didPresentRichMedia:(PWRichMedia *)richMedia { self.isPresenting = YES; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didCloseRichMedia:(PWRichMedia *)richMedia { self.isPresenting = NO; [self.richMediaQueue removeObject:richMedia]; // Present next rich media in queue if (self.richMediaQueue.count > 0) { [[[Pushwoosh media] modalRichMedia] presentRichMedia:self.richMediaQueue.firstObject]; } } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager presentingDidFailForRichMedia:(PWRichMedia *)richMedia withError:(NSError *)error { [self richMediaManager:richMediaManager didCloseRichMedia:richMedia]; } @end ``` --- # iOS push customization **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-customization/ Customize the appearance of your iOS app’s notification alerts with a notification content app extension. --- When an iOS device receives a notification containing an alert, the system displays the contents of the alert in two stages. Initially, it displays an abbreviated banner with the title, subtitle, and two to four lines of body text from the notification. If the user presses the abbreviated banner, iOS displays the full notification interface, including any notification-related actions. The system provides the interface for the abbreviated banner, but you can customize the full interface using a notification content app extension. The notification content app extension manages a view controller that displays your custom notification interface. This view controller can supplement or replace the default system interface for your notifications. You can use your view controller to: * Customize the placement of items, including the alert’s title, subtitle, and body text. * Substitute different fonts or styling for interface elements. * Display app-specific data—for example, data stored in app-specific keys of the notification’s payload. * Include custom images or branding. Your app extension must configure its view controller using the data immediately available, such as the contents of the notification and the files present in your app extension’s bundle. If you use an app group to share data between your app and your app extension, you may also use any files found in the app group. To ensure your notifications are delivered in a timely manner, configure the views as quickly as possible. Don’t perform any long-running tasks, like trying to retrieve data over the network. Caution Notification content app extensions are supported only in iOS apps. ### Add the notification content app extension to your project To add a notification content app extension to your iOS app: 1. Choose **File** > **New** > **Target** in Xcode. 2. Select **Notification Content Extension** from iOS Application Extension. 3. Click **Next**. 4. Provide a **name** for your app extension. 5. Click **Finish**. ### Add views to your view controller The template provided by Xcode includes a storyboard and a view controller for you to configure. Build your custom notification interface by adding views to your view controller. For example, use labels to display the title, subtitle, and body text of the notification. You can also add image views and views that display noninteractive content. You don’t need to provide any initial content for your views. You can add interactive controls (for example, buttons or switches) in iOS 12 and later. For more information, see Support Interactive Controls. Caution Don’t add additional view controllers to your app extension or storyboard file. Your app extension must contain exactly one view controller. ### Configure your view controller Use the `didReceive(_:)` method of your view controller to update its labels and other views. The notification payload contains the data to use when configuring your view controller. You can also use data from the other files of your app extension. [Listing 1](https://developer.apple.com/documentation/usernotificationsui/customizing_the_appearance_of_notifications#2950663) shows a version of this method that retrieves the title and body text from the notification payload and assigns the strings to two [`UILabel`](https://developer.apple.com/documentation/uikit/uilabel) controls, which are stored as outlets on the view controller. * Swift ```swift // Configuring the notification interface at runtime func didReceive(_ notification: UNNotification) { self.bodyText?.text = notification.request.content.body self.headlineText?.text = notification.request.content.title } ``` If a second notification arrives when your view controller is already visible, the system calls the `didReceive(_:)` method again with the new notification payload. ### Support interactive controls In iOS 12 and later you can enable user interactions in your custom notifications. This lets you add interactive controls, such as buttons and switches to your custom interface. To enable user interactions: 1. Open your Notification Content Extension’s `info.plist` file. 2. Add the `UNNotificationExtensionUserInteractionEnabled` key to your extension attributes. Give it a Boolean value, set to `YES`. ![Enabling user interactions in the notification extension’s info.plist file](https://docs.pushwoosh.com/ios-push-notifications-ios-push-customization-2.webp) ### Add Apple Pay to your push notification extension (example view) ### Code block example with Apple Pay button in Push Notification extension * Swift ```swift import UIKit import Foundation import UserNotifications import UserNotificationsUI import PassKit class NotificationViewController: UIViewController, UNNotificationContentExtension, PKPaymentAuthorizationViewControllerDelegate { func paymentAuthorizationViewControllerDidFinish(_ controller: PKPaymentAuthorizationViewController) { controller.dismiss(animated: true, completion: nil) } @IBOutlet weak var buyWithApplePayButton: UIButton! @IBOutlet weak var containerView: UIView! @IBOutlet weak var payInAppButton: UIButton! @IBOutlet weak var segmentCustom: UISegmentedControl! private var amount: NSDecimalNumber = 0; override func viewDidLoad() { super.viewDidLoad() buttonCustomisation() amount = NSDecimalNumber.init(value: 5) } func buttonCustomisation () { self.buyWithApplePayButton.layer.masksToBounds = true self.buyWithApplePayButton.layer.cornerRadius = 10 self.payInAppButton.layer.masksToBounds = true self.payInAppButton.layer.cornerRadius = 10 self.containerView.layer.borderColor = UIColor.gray.cgColor self.containerView.layer.borderWidth = 0.7 self.containerView.layer.masksToBounds = true self.containerView.layer.cornerRadius = 20 } func didReceive(_ notification: UNNotification) { // Configuring the notification interface at runtime } @IBAction func buyWithApplePayAction(_ sender: Any) { let request = PKPaymentRequest() request.merchantIdentifier = "merchant.com.sample.ApplePayAction" request.supportedNetworks = [PKPaymentNetwork.visa, PKPaymentNetwork.masterCard, PKPaymentNetwork.amex] request.merchantCapabilities = PKMerchantCapability.capability3DS request.countryCode = "US" request.currencyCode = "USD" request.paymentSummaryItems = [ PKPaymentSummaryItem(label: "Some Product", amount: amount) ] let applePayController = PKPaymentAuthorizationViewController(paymentRequest: request) applePayController?.delegate = self self.present(applePayController!, animated: true, completion: nil) } @IBAction func payInAppButtonAction(_ sender: Any) { print("pay in app button tapped") } @IBAction func segmentAction(_ sender: Any) { switch segmentCustom.selectedSegmentIndex { case 0: amount = NSDecimalNumber.init(value: 5) break case 1: amount = NSDecimalNumber.init(value: 10) break case 2: amount = NSDecimalNumber.init(value: 20) break default: amount = NSDecimalNumber.init(value: 5) break } } } ``` --- # iOS push primer **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-primer/ Show a customizable soft opt-in dialog before the iOS system push permission prompt to raise opt-in rates. --- A push primer is a soft opt-in dialog you show **before** the iOS system push permission prompt. iOS shows the system prompt only once per install — if the user taps **Don’t Allow**, push is lost until they re-enable it in Settings. The primer lets you explain the value first and ask at the right moment, so you spend the one-shot system prompt on users who already said yes. Available since version 7.1.1. The primer is part of `PushwooshFramework`; no extra module is required. ![Push primer dialog shown before the system permission prompt](https://docs.pushwoosh.com/ios-push-primer-1.webp) Push primer shown before the iOS system permission prompt ## How it works The primer is fully state-aware. It reads the current notification authorization status and decides what to do, so it is safe to call on every launch: * **Not determined** — shows the primer; on accept it triggers the system permission prompt. * **Authorized or provisional** — silently suppresses the primer (nothing is shown). * **Denied** — shows the primer; on accept it routes the user to the app’s notification settings (when `fallbackToSettings` is enabled). You decide **when** to call the primer (for example after onboarding or after a key action). The SDK does not impose any timing of its own, except for the optional `minInterval` throttle described below. ## Basic usage Configure the primer with a fluent builder and call `present`. The minimum setup needs a title, a message, and the two button titles. * Swift ```swift import PushwooshFramework Pushwoosh.configure.pushPrimer .title("Stay in the loop") .message("Get notified about deals and order updates first") .acceptButton("Enable notifications") .declineButton("Not now") .present() ``` Note Call the primer after the app UI is on screen (for example a short moment after launch, or at a deliberate step in your flow), so it can present over your interface. ## Styles and positions Use `style` to choose between a system alert and a custom sheet, and `position` to place the custom sheet. Each position has its own default design. | Value | Description | | -------------------- | ----------------------------------------------------------------------------- | | `.alert` | System `UIAlertController`. Position is ignored. | | `.sheet` + `.bottom` | Bottom sheet that slides up, with a grabber and full-width buttons (default). | | `.sheet` + `.top` | Compact banner that drops in from the top, like a notification. | | `.sheet` + `.center` | Centered dialog that scales and fades in. | * Swift ```swift Pushwoosh.configure.pushPrimer .style(.sheet) .position(.top) .title("Stay in the loop") .message("Get notified about deals and order updates first") .acceptButton("Enable notifications") .declineButton("Not now") .present() ``` ![Push primer in bottom, top and center positions](https://docs.pushwoosh.com/ios-push-primer-positions.webp) ## Customization All visual settings are optional — omit them to use the native defaults that adapt to light and dark mode. * Swift ```swift Pushwoosh.configure.pushPrimer .style(.sheet) .position(.center) .title("Stay in the loop") .message("Get notified about deals and order updates first") .acceptButton("Enable notifications") .declineButton("Not now") .image(UIImage(named: "PrimerHero")) // local image, or .imageURL("https://…") .backgroundColor(.systemBackground) .titleColor(.label) .messageColor(.secondaryLabel) .acceptButtonColor(.systemBlue) .acceptButtonTextColor(.white) .declineButtonColor(.clear) .declineButtonTextColor(.secondaryLabel) .cornerRadius(24) .buttonCornerRadius(14) .buttonBorderColor(.separator) .present() ``` Customization reference: | Setter | Description | | ----------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `image` / `imageURL` | A local `UIImage` or a remote URL. Rendered as a circle on the center and bottom layouts, and as the icon on the top banner. A local image takes precedence over a URL. | | `backgroundColor` | Solid background color of the card. | | `backgroundGradient` | An array of colors rendered as a soft multi-color gradient. Overrides `backgroundColor`. | | `titleColor` / `messageColor` | Title and message text colors. | | `acceptButtonColor` / `acceptButtonTextColor` | Accept button background and text colors. The accept color also tints the default icon. | | `declineButtonColor` / `declineButtonTextColor` | Decline button background and text colors. | | `cornerRadius` | Corner radius of the card. | | `buttonCornerRadius` / `buttonBorderColor` | Corner radius and border color of both buttons. | Note The primer shows exactly the strings you pass. For multiple languages, pass localized strings (for example with `NSLocalizedString`). ## Behavior settings ### Settings fallback By default, when notifications are already denied the primer is shown and the accept button takes the user to the app’s notification settings. Pass `false` to suppress the primer entirely in the denied state instead. ```swift .fallbackToSettings(false) ``` ### Display frequency By default the primer has no built-in throttling — it shows whenever you call `present` (and is auto-suppressed once notifications are authorized). Use `minInterval` to cap how often the primer reappears. The last shown time is persisted across launches. ```swift .minInterval(7 * 24 * 60 * 60) // show at most once a week ``` ## Handling the result Pass a completion to `present` to react to the outcome. * Swift ```swift Pushwoosh.configure.pushPrimer .title("Stay in the loop") .message("Get notified about deals and order updates first") .acceptButton("Enable notifications") .declineButton("Not now") .present { outcome in switch outcome { case .accepted: break // shown, user accepted, system prompt requested case .declined: break // shown, user declined case .suppressed: break // not shown (already authorized, or throttled) case .redirectedToSettings: break // denied state, user sent to Settings @unknown default: break } } ``` The final system-prompt result (the granted/denied state and the device token) arrives through the regular registration callbacks — the primer reuses `registerForPushNotifications` on accept and does not duplicate that chain. ## References [iOS SDK API Reference ](https://pushwoosh.github.io/pushwoosh-ios-sdk/)Complete technical documentation covering all public classes, methods, and properties. [Customizing iOS SDK ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/)Other ways to tailor the Pushwoosh iOS SDK to your app. [Show an in-app before the push permission prompt ](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/)A cross-platform HTML alternative that covers iOS and Android with one template. --- # iOS push stories **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-stories/ Show a full-screen, Instagram-style stories experience inside an expanded iOS push notification. --- Push stories turn an expanded push notification into a full-screen, Instagram-style stories experience: full-bleed images, top progress bars, auto-advancing pages, tap-to-navigate, and a button with a deep link. They are rendered by a Notification Content Extension using the standalone `PushwooshNotificationUI` module — you subclass one view controller and the SDK handles parsing, image loading, progress, timing, navigation, and deep links. Available since version 7.0.46. ![Push stories playing in an expanded notification](https://docs.pushwoosh.com/ios-push-stories-demo.webp) ## 1. Add a Notification Content Extension In Xcode, choose **File > New > Target…**, select **Notification Content Extension**, and name it (for example **StoriesContentExtension**). ![Adding a Notification Content Extension target in Xcode](https://docs.pushwoosh.com/ios-push-stories-1.webp) ## 2. Add the PushwooshNotificationUI module `PushwooshNotificationUI` is a standalone module with no other Pushwoosh dependencies, so it stays small inside the extension process. Add it to the **content extension target** (not the app target). **Swift Package Manager** ![Adding the PushwooshNotificationUI package to the extension target](https://docs.pushwoosh.com/ios-push-stories-spm.webp) **CocoaPods** ```ruby target 'StoriesContentExtension' do use_frameworks! pod 'PushwooshXCFramework/PushwooshNotificationUI' end ``` ## 3. Subclass the stories view controller Replace the generated `NotificationViewController` body with a subclass of `PushwooshStoriesViewController`. That is the whole integration. * Swift ```swift import PushwooshNotificationUI class NotificationViewController: PushwooshStoriesViewController {} ``` ## 4. Configure the extension Info.plist In the content extension’s **Info.plist**, set the following keys under `NSExtension > NSExtensionAttributes`: ```xml UNNotificationExtensionCategory PW_STORIES UNNotificationExtensionUserInteractionEnabled UNNotificationExtensionDefaultContentHidden UNNotificationExtensionInitialContentSizeRatio 1.5 ``` Note `UNNotificationExtensionCategory` must match the category you send in the push (`PW_STORIES`). `UNNotificationExtensionInitialContentSizeRatio` sets the initial height ratio of the expanded view. ## 5. Send a push stories notification Send a notification whose category is `PW_STORIES` and whose custom data carries a `pw_stories` block. Use the dedicated `ios_category_custom` field for the category and the `data` field for the stories payload. ```json { "request": { "application": "APPLICATION_CODE", "auth": "API_ACCESS_TOKEN", "notifications": [ { "send_date": "now", "content": "Tap to explore", "ios_title": "Push Stories", "ios_category_custom": "PW_STORIES", "ios_root_params": { "aps": { "mutable-content": 1 } }, "data": { "pw_stories": { "pages": [ { "image": "https://example.com/story-1.jpg", "duration": 5.0, "link": "yourapp://page1", "button_title": "Get started", "title": "Welcome", "subtitle": "Swipe to explore what's new" }, { "image": "https://example.com/story-2.jpg", "duration": 4.0, "link": "yourapp://page2", "button_title": "Learn more", "title": "Stay in the loop", "subtitle": "Updates, tips and more" } ] } } } ] } } ``` Each page supports the following fields. Only `image` is required; the rest are optional. | Field | Description | | -------------- | ------------------------------------------------------------------------------------ | | `image` | URL of the full-screen image for the page. | | `duration` | Seconds the page stays on screen before auto-advancing. Defaults to about 5 seconds. | | `link` | Deep link opened when the page button is tapped. | | `button_title` | Title of the page button. | | `title` | Title text overlaid on the page. | | `subtitle` | Subtitle text overlaid on the page. | Note A missing, empty, or malformed `pw_stories` block does not crash the extension — it falls back to the default notification content. `mutable-content: 1` is only required for the optional media pre-cache path described below. ## Customization Override properties on your subclass to adjust the experience. All have sensible defaults. * Swift ```swift import PushwooshNotificationUI class NotificationViewController: PushwooshStoriesViewController { override var storyAspectRatio: CGFloat { 1.5 } // keep in sync with InitialContentSizeRatio override var hapticsEnabled: Bool { true } override var longPressToPauseEnabled: Bool { true } override var crossfadesBetweenPages: Bool { true } override var loopsAfterLastPage: Bool { false } } ``` | Property | Default | Description | | ------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------- | | `storyAspectRatio` | `1.5` | Aspect ratio (height ÷ width) of the stories area. Keep it in sync with `UNNotificationExtensionInitialContentSizeRatio`. | | `hapticsEnabled` | `false` | Play a light tactile tap on tap-zone navigation. | | `longPressToPauseEnabled` | `false` | Press and hold pauses the current page; lifting resumes. | | `crossfadesBetweenPages` | `false` | Cross-dissolve between pages instead of cutting hard. Falls back to an instant change when Reduce Motion is on. | | `loopsAfterLastPage` | `false` | Restart from the first page after the last one finishes. | | `appGroupIdentifier` | `nil` | App Group shared with a Notification Service Extension for media pre-cache (see below). | You can also override `showDefaultContent(for:)` to customize the fallback shown when the payload is missing or malformed (by default it shows the alert body). ### Media pre-cache For an instant, offline first frame, share an App Group between your Content Extension and a Notification Service Extension. Override `appGroupIdentifier` on the stories controller, then pre-download the media from your Service Extension’s `didReceive(_:withContentHandler:)`: * Swift ```swift import PushwooshNotificationUI PushwooshStoriesMediaPrefetcher.prefetch( userInfo: request.content.userInfo, appGroupIdentifier: "group.com.example.app" ) { contentHandler(bestAttemptContent) } ``` Enable the **App Groups** capability on both extensions with the same group identifier, and send `mutable-content: 1` so the Service Extension runs. Without an App Group, media is cached in the extension’s `tmp` directory instead. ### Lifecycle and analytics callbacks Set `storiesDelegate` to observe story events — page impressions, button taps, completion, and fallback. Conform to `PushwooshStoriesDelegate`; every method is optional, so implement only the ones you need. * Swift ```swift import PushwooshNotificationUI class NotificationViewController: PushwooshStoriesViewController, PushwooshStoriesDelegate { override func viewDidLoad() { super.viewDidLoad() storiesDelegate = self } func storiesViewController(_ controller: PushwooshStoriesViewController, didStartWithPageCount pageCount: Int) {} func storiesViewController(_ controller: PushwooshStoriesViewController, didShow page: StoryPage, at index: Int) {} func storiesViewController(_ controller: PushwooshStoriesViewController, didTapActionFor page: StoryPage, at index: Int) {} func storiesViewControllerDidFinish(_ controller: PushwooshStoriesViewController) {} func storiesViewControllerDidShowFallback(_ controller: PushwooshStoriesViewController) {} } ``` | Callback | When it fires | | --------------------------------------- | ------------------------------------------------------------------------ | | `didStartWithPageCount:` | A valid stories payload was parsed and playback is about to begin. | | `didShow:at:` | A page became visible. Use it for per-page impressions. | | `didTapActionFor:at:` | The user tapped the call-to-action button. | | `storiesViewControllerDidFinish:` | The last page finished playing. | | `storiesViewControllerDidShowFallback:` | The payload was missing or malformed and the fallback content was shown. | The `StoryPage` passed to the callbacks exposes the page’s `imageURL`, `duration`, `link`, `buttonTitle`, `title`, and `subtitle`. ### Navigation Tap the right third of the screen to go to the next page and the left third to go back. Horizontal swipe is intentionally not used, because it conflicts with the system’s notification-dismiss gesture. Tapping the page button opens its deep link and dismisses the notification. ## References [iOS SDK API Reference ](https://pushwoosh.github.io/pushwoosh-ios-sdk/)Complete technical documentation covering all public classes, methods, and properties. [Messages API ](https://docs.pushwoosh.com/developer/api-reference/messages-api/)Send notifications, including the data and ios_category_custom fields, through the Pushwoosh API. --- # iOS rich notifications integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-rich-notifications-integration/ Attach images, gifs, and videos to your push notifications --- Starting with iOS 10, you can attach a static or animated image or even a video to push notifications. It will be displayed right in the notification when the user force-taps it. Enabling this functionality is very simple, with very little coding involved. Let’s do it! ## 1. Creating Notification Service Extension First create a [Notification Service Extension](https://developer.apple.com/reference/usernotifications/unnotificationserviceextension). This extension downloads the content that will be shown to the user. Add new target to your project (File -> New -> Target) and create *Notification Service Extension*. ![Creating Notification Service Extension](https://docs.pushwoosh.com/ios-rich-notification-1.webp) Caution Notification Service Extension has a separate Apple App ID and Provisioning profile! Note that notification extension has its own Bundle Id (*ex:*`com.pushwoosh.example.NotificationService`) as well as its own **Apple App ID** and **Provisioning profile** which must be setup in Apple Developer Portal separately ## 2. Notification Service Extension code The code downloads the attachment and calls the notification content handler.\ Just Copy & Paste it to your extension. * Swift ```swift import UserNotifications import PushwooshFramework class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { PWNotificationExtensionManager.shared().handle(request, contentHandler: contentHandler) } } ``` * Objective-C ```objective-c #import "PWNotificationExtensionManager.h" @interface NotificationService : UNNotificationServiceExtension @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; } @end ``` ## 3. Allowing non-secure attachment URL’s Notification Service Extension is a separate binary and **has its own Info.plist** file.\ Add **App Transport Security Settings** with **Allow Arbitrary Loads** flag set to `true` to **extension’s Info.plist** file. **Info.plist**: ```xml NSAppTransportSecurity NSAllowsArbitraryLoads ``` ## 4. Sending a rich notification In order to send a rich notification just specify the file’s URL in the **BANNER URL** field Woosh! Force-tap the notification and you are all done! [](https://docs.pushwoosh.com/ios-rich-notification-3.webm "Example") Tip Learn how to [make videos in your iOS Rich Push Notifications autoplay with force touch](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#autoplay-a-video-sent-in-a-rich-notification-with-force-touch) ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Purchases from iOS in-app messages **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/purchases-from-ios-in-app-messages/ How to implement in-app purchases from Rich Media displayed to iOS app users --- To let your users make purchases right from the in-app messages displayed in your iOS app, you can implement the out-of-the-box solution for your Rich Medias and add callbacks to get more info about in-app purchases. Note Available for the **6.4.5 iOS SDK version and further**. ## Rich Media JavaScript implementation To implement in-app purchases in your Rich Medias, use a simple **JavaScript function call**: ```javascript pushwooshImpl.makePurchaseWithIdentifier("Premium"); // product identifier specified in your App Store product information ``` ## AppDelegate methods for callbacks If you need callbacks to be executed on a purchase, implement the **PWPurchaseDelegate protocol** as follows: 1. Make your project’s AppDelegate conform to the PWPurchaseDelegate protocol: ```objective-c @interface AppDelegate : PWAppDelegate ``` 2. Implement methods of PWPurchaseDelegate in your AppDelegate: ```objective-c #pragma mark - Purchase delegate methods - (void) onPWInAppPurchaseHelperPaymentComplete:(NSString*) identifier { NSLog(@“Custom purchase delegate -- payment complete for identifier: %@“, identifier); } -(void)onPWInAppPurchaseHelperProducts:(NSArray* _Nullable)products { NSLog(@“Custom purchase delegate -- retrieved list of products”); } -(void)onPWInAppPurchaseHelperPaymentFailedProductIdentifier:(NSString* _Nullable)identifier error:(NSError* _Nullable)error { NSLog(@“Custom purchase delegate -- payment failed with identifier %@ and error %@“, identifier, error.description); } ``` ### List of available PWPurchaseDelegate methods #### List of products ```objective-c @protocol PWPurchaseDelegate // Detailed information about list of products in your app -(void)onPWInAppPurchaseHelperProducts:(NSArray* _Nullable)products; @end ``` #### Successful transaction ```objective-c @protocol PWPurchaseDelegate <NSObject> // A successfully processed transaction -(void)onPWInAppPurchaseHelperPaymentComplete:(NSString* _Nullable)identifier; @end ``` #### Failed transaction ```objective-c @protocol PWPurchaseDelegate <NSObject> // A failed transaction -(void)onPWInAppPurchaseHelperPaymentFailedProductIdentifier:(NSString* _Nullable)identifier error:(NSError* _Nullable)error; @end ``` #### Promoted from App Store ```objective-c @protocol PWPurchaseDelegate // An in-app purchase is initiated from the App Store, and the transaction continues in your app. -(void)onPWInAppPurchaseHelperCallPromotedPurchase:(NSString* _Nullable)identifier; @end ``` #### Failed restoring completed transactions ```objective-c @protocol PWPurchaseDelegate // An error occurred while restoring transactions -(void)onPWInAppPurchaseHelperRestoreCompletedTransactionsFailed:(NSError * _Nullable)error; @end ``` Note If your needs are not covered with the methods specified in this guide, please reach out to our [Customer Support team](https://help.pushwoosh.com/hc/en-us/requests/new?utm_content=pwwebsite&utm_term=footer&utm_campaign=support), and we’ll be happy to provide you with further guidance. --- # iOS code examples **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/pushwoosh-sdk-samples-for-ios/ This section provides sample iOS applications that demonstrate the functionality of the Pushwoosh SDK. --- With Pushwoosh samples for iOS, you can explore Pushwoosh functionality without integrating the SDK into your project, or review how the Pushwoosh SDK is implemented in our example projects ## Prerequisites Before using the examples, ensure you have the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Pushwoosh sample application This project is a basic iOS application with the Pushwoosh SDK integrated, showcasing key features such as: * Receiving push notifications. * Tracking push notification delivery. * Managing user tags. * Sending events * User registration Use the sample app as a learning tool or a foundation for your development. ### 1. Download the sample app Clone the repository: ```bash git clone https://github.com/Pushwoosh/pushwoosh-ios-sample.git ``` ### 2. Configure the project 1. Open the project in Xcode. 2. Add `https://github.com/Pushwoosh/Pushwoosh-XCFramework` as Swift Package Dependency. 3. Set the **bundle identifier** for the main target (PushwooshSampleApp) to match your Pushwoosh project (e.g., `com.pushwoosh.PushwooshSampleApp`). 4. Use the same **bundle identifier** for the NotificationService target, appending `.NotificationService` (e.g., `com.pushwoosh.PushwooshSampleApp.NotificationService`). 5. In `Info.plist`, set the: * `Pushwoosh_APPID` key to the Pushwoosh Application Code * `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ### 3. Run the project 1. Build and run the project. 2. Navigate to the Settings tab in the app. 3. Tap the **Register for Pushes** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. ## Pushwoosh iOS examples Explore our [pushwoosh-quickstart-ios](https://github.com/Pushwoosh/pushwoosh-quickstart-ios) repository for fully documented code examples covering various SDK features. These samples are ready to use as references or starting points for your own projects. --- # Setting up badges **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges/ This guide describes the process of implementing Badges for Pushwoosh iOS SDK --- Note To set badges for Cordova or React Native plugins, please refer to the corresponding guides: * [Badges for Cordova](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/cordova/setting-up-badges-for-cordova) * [Badges for React Native](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/react-native/setting-up-badges-for-react-native) Starting with version 6.4.0 of Pushwoosh iOS SDK, the process of managing and setting badges through push notifications has changed. If you were previously using the badges number to indicate the number of received push notifications in your application, you have to migrate to newer version of iOS SDK (6.4.0 or later). The older versions will be supported until the end of the second quarter of 2022. ## What determines the badge value The badge always comes from the push itself. No Pushwoosh feature sets or clears it on its own. Sending `"ios_badges"` in the [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) request (or the **badges** field in Control Panel) sets both `aps.badge` (the absolute value) and `aps.pw_badge` (the relative delta) in the iOS payload. Android carries the equivalent value in `pw_badges` and needs the [`com.pushwoosh:pushwoosh-badge`](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#using-badge-number-on-android) module to apply it on supported launchers. Saving a push to [Message Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/) never sets or changes the badge. The two are independent effects of the same push. Caution The relative **+1**/**-1** badge value is only applied by the Notification Service Extension, and only when it’s installed and configured with a matching App Group as described below. Without that setup, the OS applies the absolute `aps.badge` value from the payload as-is. There’s no auto-increment. ## How to set up badges To set up badges, follow the steps below: 1. Add Notification Service Extension (File -> New -> Target…) 2. Select the “Notification Service Extension” ![Adding Notification Service Extension](https://docs.pushwoosh.com/ios-push-notifications-setting-up-badges-1.webp) 3. Replace the generated `NotificationService` class with a subclass of `PushwooshNotificationServiceExtension`. Pushwoosh handles the badge counting, delivery event, attachment download, and the timeout fallback for you: * Swift ```swift import PushwooshFramework class NotificationService: PushwooshNotificationServiceExtension {} ``` * Objective-C ```objective-c #import @interface NotificationService : PushwooshNotificationServiceExtension @end @implementation NotificationService @end ``` 4. Add the App Groups capability to **both** targets — the main app and the Notification Service Extension — and use the same App Group for each. 5. Add the App Group ID to the **main app’s** `info.plist`: ```plaintext PW_APP_GROUPS_NAME group.com.example.demoapp_example ``` ![Info.plist](https://docs.pushwoosh.com/shared-12.webp) The extension inherits this value from the host app, so you don’t need to repeat it in the extension’s `info.plist` — add it there only to override the host value. If you prefer not to use the **info.plist** file, provide the App Group in code by overriding `pushwooshAppGroupsName` in your NotificationServiceExtension class: * Swift ```swift override func pushwooshAppGroupsName() -> String? { "group.com.example.demoapp_example" } ``` * Objective-C ```objective-c - (NSString *)pushwooshAppGroupsName { return @"group.com.example.demoapp_example"; } ``` [Example ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/badges/badges) ## Send a push notification with badges If you want to send push notification with badges from Pushwoosh Control Panel, just add a desired badge value to the **badges** text field as shown in the picture below: ![Badges value](https://docs.pushwoosh.com/ios-push-notifications-setting-up-badges-5.webp) Note You can use the following value formats to send badges correctly: **+1, -1 or 5.** If you want to increment badge value, use **”+”** sign**,** if you want to decrement badges, use **”-”** sign**.** You can also set the exact number by using e.g. **5** with no sign. ## Share your feedback with us Your feedback helps us create a better experience, so we would love to hear from you if you have any issues during the SDK integration process. If you face any difficulties, please do not hesitate to share your thoughts with us [via this form](https://docs.google.com/forms/d/e/1FAIpQLSd_0b8jwn-V_JmoPLIxIFYbHACCQhrzidOZV3ELywoQPXRSxw/viewform). --- # Setting up Pushwoosh InboxKit iOS **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/ Modern UIKit inbox UI for the Pushwoosh iOS SDK. Banner, captioned, classic, carousel, video, and Apple Wallet card layouts with inline CTAs, pinning, and swipe-to-delete. --- *Available since iOS SDK [7.0.40](https://github.com/Pushwoosh/pushwoosh-ios-sdk/releases/tag/7.0.40).* Pushwoosh InboxKit ships a modern UIKit inbox screen on top of the existing inbox backend. Six default cell layouts cover the common content-card shapes — from simple banners to image carousels, inline video, and Apple Wallet passes — inline CTA buttons handle the most common interactions, and the entire surface is open for subclassing if you need a bespoke look. ![InboxKit feed showing banner, captioned, classic, carousel, video and Apple Wallet cards](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-feed.webp) Default InboxKit feed with banner, captioned, classic, carousel, video, and Apple Wallet cards. ## When to use InboxKit Use InboxKit for any new iOS integration. It is the recommended replacement for the older Objective-C [PushwooshInboxUI](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/) module. InboxKit gives you: * Six built-in cell types — banner, captioned, classic, carousel, video, and Apple Wallet — selected per message via the payload’s `displayType`, or forced from code via `attributes.forceCellKind`. See [Card types](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/#card-types) for the full list. (The Apple Wallet card is iOS-only.) * Inline CTA buttons with a typed `PushwooshInboxButtonAction` enum (`openURL`, `dismiss`, `markRead`, `custom`). The SDK handles the first three automatically; your delegate routes `custom` to your own logic. * Pinning support: messages with `actionParams["pinned"] == true` float to the top of the feed and render a pin glyph. * Swipe-to-delete, pull-to-refresh, automatic mark-as-read on disappear — all toggleable via `PushwooshInboxKitAttributes`. * Persistent storage: deletes and read state survive a process restart even if the network call has not been acknowledged yet. * An open `PushwooshInboxCell` base class for fully custom layouts. The server contract is unchanged — the same Pushwoosh inbox backend, payloads, and dashboard tooling work as before. ## Choose your integration method * [**Set up InboxKit with Swift Package Manager**](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-swift-package-manager/) — recommended for new projects. * [**Set up InboxKit with CocoaPods**](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-cocoapods/) — for projects already using CocoaPods. ## Card types InboxKit picks a cell layout per message. The default resolver reads `displayType` from the push payload — put it inside the `data` object, which the SDK delivers under `actionParams`. When `displayType` is missing, the resolver falls back to a heuristic: image + no title → banner, image + title + body → captioned, otherwise classic. To force one layout for the whole feed from code, set `attributes.forceCellKind`. Each rich layout degrades gracefully: if a mandatory field is absent or malformed, the card falls back to `classic` rather than rendering an empty placeholder (and a `WARN` is logged with the reason). `classic` is the terminal fallback and renders whatever the message carries; the message editor is expected to fill its title, body and icon. | `displayType` | Layout | Required payload field | Degrades to | | ------------- | ------------------------------------------------- | -------------------------------------------------------------------- | -------------------------------------------------- | | `banner` | Full-bleed image, no text | image (`inbox_image` or `data.image`) | `classic` when no image | | `captioned` | Image on top, title + body below | image (`inbox_image` or `data.image`), message `title` and `content` | `classic` when the image, title or body is missing | | `classic` | Coloured initial avatar + title + body | — (title, body and icon expected) | — | | `carousel` | Swipeable multi-image gallery | message `title` and `content`, `data.carousel` (1–5 slides) | `classic` when no slides or no title/body | | `video` | Poster with play badge, full-screen player on tap | `data.video` (`url` + optional `poster`) | `classic` when no descriptor | | `wallet` | ”Add to Apple Wallet” button (iOS only) | `data.wallet` (`.pkpass` URL) | `classic` when no pass URL | ![InboxKit banner card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-banner.webp) **Banner card** ![InboxKit captioned card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-captioned.webp) **Captioned card** ![InboxKit classic card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-classic.webp) **Classic card** ![InboxKit carousel card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-carousel.webp) **Carousel card** ![InboxKit video card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-video.webp) **Video card** ![InboxKit Apple Wallet card](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-card-wallet.webp) **Apple Wallet card** Banner, captioned, and classic cards are driven by the standard message fields (image, title, body) plus the optional `buttons` array — see [Add inline CTA buttons](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/#add-inline-cta-buttons). The carousel, video, and Apple Wallet cards carry extra structured data inside `data`, documented below. ### Carousel card A carousel renders several images from a single message — a swipeable gallery with optional per-slide captions and tap destinations. Slides live in `data.carousel`. Each slide needs an `image`; `title` (caption overlay) and `url` (deep link opened on tap) are optional. A slide without an image is dropped; a tap on a slide with no `url` falls through to the message’s default row action. At most **5 slides** are shown — extra slides are dropped (a slide without an image does not use up a place). The message `title` and `content` are mandatory for this layout; without them the card degrades to `classic`. POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "New arrivals", "content": "Swipe through this week's drops", "inbox_days": 7, "data": { "displayType": "carousel", "carousel": [ { "image": "https://cdn.example.com/inbox/1.jpg", "title": "New in", "url": "myapp://product/1" }, { "image": "https://cdn.example.com/inbox/2.jpg", "title": "On sale", "url": "myapp://product/2" }, { "image": "https://cdn.example.com/inbox/3.jpg" } ] }, "platforms": [1] }] } } ``` ### Video card A video card shows a poster image with a play badge; tapping it opens a full-screen player (sound on, even with the silent switch engaged). The descriptor lives in `data.video`: `url` is required and must be an `http`/`https` stream or file; `poster` is an optional preview image. POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Watch the reveal", "content": "Tap to play", "inbox_days": 7, "data": { "displayType": "video", "video": { "url": "https://cdn.example.com/inbox/clip.mp4", "poster": "https://cdn.example.com/inbox/poster.jpg" } }, "platforms": [1] }] } } ``` ### Apple Wallet card The Apple Wallet card shows an optional hero image, title, and body above the official **Add to Apple Wallet** button. Tapping the button downloads the `.pkpass` and presents the system add-passes sheet. Use it to deliver coupons, loyalty cards, tickets, or boarding passes straight from the inbox. The card is iOS / Mac Catalyst only — on other platforms the message renders as a classic card. The pass URL lives in `data.wallet`, either as a bare string or as an object with a `pass` field. An optional `data.image` adds the hero image. The button hides itself automatically when there is no pass URL or the device cannot add passes. POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Your loyalty card is ready", "content": "Add it to Apple Wallet in one tap", "inbox_days": 7, "data": { "displayType": "wallet", "image": "https://cdn.example.com/inbox/loyalty.png", "wallet": "https://passes.example.com/v1/passes/pass.com.example.loyalty/abc123?token=…" }, "platforms": [1] }] } } ``` The outcome is reported to your delegate: ```swift extension MyInboxHost: PushwooshInboxKitDelegate { func inboxKit(_ vc: PushwooshInboxKitViewController, didAddWalletPassFor message: PWInboxMessageProtocol) { // The pass is now in the user's Wallet — show a confirmation if you like. } func inboxKit(_ vc: PushwooshInboxKitViewController, didFailToAddWalletPassFor message: PWInboxMessageProtocol, error: Error?) { // Download failed — surface a retry, log, etc. } } ``` Both callbacks are optional (they carry default empty implementations). A user cancelling the system sheet is neither success nor failure, so no callback fires in that case. ## Accessibility InboxKit cells are VoiceOver-ready out of the box. Banner, captioned, and classic cards expose their title, body, and date through the underlying labels, and inline CTA buttons read their own titles. The rich cards add explicit semantics: * **Video** — the poster is exposed as a single button element labelled “Play video” (traits `.button` + `.startsMediaSession`), so VoiceOver announces it as a media control rather than a plain image. * **Carousel** — each slide is a button element whose accessibility label is the slide’s caption, or “Slide” when it has none. The page indicator announces the current position as “*n* of *total*”. * **Apple Wallet** — the **Add to Apple Wallet** button is Apple’s standard `PKAddPassButton`, which carries its own localized VoiceOver label. For UI testing and automation, two stable `accessibilityIdentifier`s are set: `inboxkit.video.play` on the video poster and `inboxkit.wallet.add` on the Wallet button. ## Read custom data from a message To make a push appear in the inbox, the [Messages API](https://docs.pushwoosh.com/developer/api-reference/messages-api/) `createMessage` request must include `inbox_image`, `inbox_date`, or `inbox_days` — without one of those fields the push is delivered as a regular notification and never reaches the inbox feed. Free-form custom data goes under the `data` key, which the SDK delivers to the client as the `u` parameter: POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "Summer sale", "content": "30% off everything — limited time only", "inbox_image": "https://cdn.example.com/inbox/summer.png", "inbox_days": 7, "data": { "displayType": "captioned", "promo_id": "SUMMER2026", "screen": "promo_details" }, "platforms": [1] }] } } ``` The SDK exposes that object on the inbox message through `actionParams`. Read it from the delegate when the user taps the row or an inline CTA: ```swift extension MyInboxHost: PushwooshInboxKitDelegate { func inboxKit(_ vc: PushwooshInboxKitViewController, didSelect message: PWInboxMessageProtocol) -> Bool { guard let params = message.actionParams as? [String: Any] else { return true } // The custom `data` object arrives under the "u" key — // either as a nested dictionary or as a JSON-encoded string, // depending on how the payload was built upstream. let custom: [String: Any]? = { if let dict = params["u"] as? [String: Any] { return dict } if let raw = params["u"] as? String, let bytes = raw.data(using: .utf8), let parsed = try? JSONSerialization.jsonObject(with: bytes) as? [String: Any] { return parsed } return nil }() if let promoId = custom?["promo_id"] as? String { navigateToPromo(promoId) return false // we handled the tap; SDK should not run the default action } return true } } ``` The same `actionParams["u"]` lookup works inside `inboxKit(_:didTapButton:onMessage:)` for inline CTA buttons. For the typed CTA cases (`openURL`, `dismiss`, `markRead`) the SDK already performs the default action — return `true` to keep that behavior, or `false` to suppress it and run your own. ## Add inline CTA buttons A message can carry up to three inline call-to-action buttons. Buttons live alongside other custom data inside `data` as a `buttons` array. The SDK auto-renders them inside the captioned and classic cells: POST https://api.pushwoosh.com/json/1.3/createMessage ```json { "request": { "application": "XXXXX-XXXXX", "auth": "API_TOKEN", "notifications": [{ "send_date": "now", "ios_title": "New promo card", "content": "Tap a button to claim or save", "inbox_image": "https://cdn.example.com/inbox/promo.png", "inbox_days": 7, "data": { "displayType": "captioned", "promo_id": "SUMMER2026", "buttons": [ { "title": "Claim", "url": "https://example.com/promo/SUMMER2026" }, { "title": "Read", "action": "markRead" }, { "title": "Save", "action": "custom", "tag": "save_promo" } ] }, "platforms": [1] }] } } ``` Each button object has these fields: | Field | Type | When | | ------------- | ------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `title` | string | Required. Visible button label. | | `url` | string | A non-empty parseable URL produces an `openURL` action. The SDK opens it via `UIApplication.shared.open` unless your delegate suppresses it. | | `action` | string | Explicit action token: `dismiss` (removes the message from the feed), `markRead` (marks the message read), or `custom` (host-handled). Case-insensitive. | | Anything else | any | When `action` is `custom`, every key on the button object except `title` and `action` is forwarded to your delegate as the custom payload — agree on a key with the marketer (e.g. `tag`) and dispatch on it. | Resolution priority: explicit `action` token first, then `url` if non-empty, otherwise the button falls into `custom` carrying the full payload (minus `title` and `action`). Intercept taps from your delegate. The `button.action` property is the typed `PushwooshInboxButtonAction` enum: ```swift extension MyInboxHost: PushwooshInboxKitDelegate { func inboxKit(_ vc: PushwooshInboxKitViewController, didTapButton button: PushwooshInboxButton, onMessage message: PWInboxMessageProtocol) -> Bool { switch button.action { case .openURL(let url): // Default behavior is fine — let SDK open the URL. return true case .dismiss, .markRead: // SDK handles both. Return false if you want to override. return true case .custom(let payload): // Marketer-defined custom button. Dispatch on a key you agreed on. if let tag = payload["tag"] as? String { switch tag { case "save_promo": saveCurrentPromoLocally(message: message) default: break } } return true // ignored for custom — SDK never runs a default action here } } } ``` --- # Set up InboxKit with CocoaPods **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-cocoapods/ How to integrate Pushwoosh InboxKit into your iOS app with CocoaPods. Adds modern UIKit inbox cards on top of the existing Pushwoosh backend. --- *Available since iOS SDK [7.0.40](https://github.com/Pushwoosh/pushwoosh-ios-sdk/releases/tag/7.0.40).* InboxKit ships as an optional subspec of the umbrella `PushwooshXCFramework` pod. You need the main SDK to be already integrated; if you are starting from scratch, follow the [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/) first. ## Add the InboxKit pod 1. Open your `Podfile` and add the InboxKit subspec to your app target: ```ruby target 'MyApp' do use_frameworks! pod 'PushwooshXCFramework' pod 'PushwooshXCFramework/PushwooshInboxKit' end ``` 2. Run `pod install` from your project directory: ```bash pod install ``` 3. Open the generated `.xcworkspace` file. InboxKit is now linked alongside the main SDK. ## Show the inbox Add the inbox controller to any navigation flow. The default configuration is enough to get a working inbox with the three standard cell types: * Swift ```swift import PushwooshInboxKit let inboxVC = PushwooshInboxKitViewController() navigationController?.pushViewController(inboxVC, animated: true) ``` * Objective-C ```objective-c @import PushwooshInboxKit; PushwooshInboxKitViewController *inboxVC = [PushwooshInboxKitViewController new]; [self.navigationController pushViewController:inboxVC animated:YES]; ``` ## Customize the inbox In Swift, configure the controller through the `PushwooshInboxKitAttributes` value type. In Objective-C, use the `@objc`-friendly setters on the controller — `PushwooshInboxKitAttributes` is a Swift struct and is not bridged. * Swift ```swift var attributes = PushwooshInboxKitAttributes() attributes.pullToRefreshEnabled = true attributes.swipeToDeleteEnabled = true attributes.pinningEnabled = true attributes.style.unreadBadgeColor = .systemBlue attributes.style.titleFont = .systemFont(ofSize: 17, weight: .semibold) let inboxVC = PushwooshInboxKitViewController(attributes: attributes) ``` * Objective-C ```objective-c PushwooshInboxKitViewController *inboxVC = [PushwooshInboxKitViewController new]; [inboxVC setBackgroundColor:[UIColor systemBackgroundColor]]; [inboxVC setEmptyMessage:@"You have no messages yet"]; ``` The `Style` struct exposes all colors, fonts, corner radii and the date formatter used by the default cells. Every value is an Apple semantic color by default, so the inbox reacts to system dark mode automatically. ![Custom-styled InboxKit feed with brand colors applied to title and unread indicator](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-custom.webp) Captioned cell with a custom theme applied through `PushwooshInboxKitAttributes.Style`. ## Handle taps and refreshes Conform to `PushwooshInboxKitDelegate` to react to user actions and refresh events. Every method has a default implementation, so you only override what you need: * Swift ```swift final class InboxCoordinator: NSObject, PushwooshInboxKitDelegate { func inboxKit(_ vc: PushwooshInboxKitViewController, didSelect message: PWInboxMessageProtocol) -> Bool { // Return true to let the SDK open the message URL or richmedia. // Return false if you handled the tap entirely (e.g. routed to a custom screen). return true } func inboxKit(_ vc: PushwooshInboxKitViewController, didRefreshWith messages: [PWInboxMessageProtocol], error: Error?) { // Show your own empty / error state here if needed. } } inboxVC.delegate = inboxCoordinator ``` The SDK ships bulk operations as `@objc` methods on the controller, so you can wire them straight to a `UIBarButtonItem`: * Swift ```swift let markAll = UIBarButtonItem( image: UIImage(systemName: "checkmark.circle"), style: .plain, target: inboxVC, action: #selector(PushwooshInboxKitViewController.markAllAsRead) ) let clearRead = UIBarButtonItem( image: UIImage(systemName: "trash"), style: .plain, target: inboxVC, action: #selector(PushwooshInboxKitViewController.clearReadMessages) ) inboxVC.navigationItem.rightBarButtonItems = [clearRead, markAll] ``` Persistence Mark-as-read, mark-all-as-read, delete, and clear-read all persist to the local Pushwoosh inbox storage before the network request is sent. State survives a process restart even when the backend has not yet acknowledged the change. ## Next steps [PushwooshInboxKit API reference ](https://pushwoosh.github.io/pushwoosh-ios-sdk/PushwooshInboxKit/documentation/pushwooshinboxkit/)Generated DocC documentation for every public type. --- # Set up InboxKit with Swift Package Manager **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/set-up-inboxkit-with-swift-package-manager/ How to integrate Pushwoosh InboxKit into your iOS app with Swift Package Manager. Modern UIKit inbox cards on top of the existing Pushwoosh backend. --- *Available since iOS SDK [7.0.40](https://github.com/Pushwoosh/pushwoosh-ios-sdk/releases/tag/7.0.40).* InboxKit ships as a separate library product inside the `Pushwoosh-XCFramework` Swift package. You need the main SDK to be already integrated; if you are starting from scratch, follow the [Basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/) first. ## Add the InboxKit package product 1. Open your project in Xcode and navigate to the project’s settings → **Package Dependencies**, then press the **+** button. 2. Enter the following Package URL: ```bash https://github.com/Pushwoosh/Pushwoosh-XCFramework ``` 3. Set up the **Dependency Rule** to **Up to Next Major Version** with the latest stable version, then click **Add Package**. 4. On the package selection screen, choose at least the following products and add them to your main app target: * **PushwooshFramework** (required) * **PushwooshCore** (required) * **PushwooshBridge** (required) * **PushwooshInboxKit** (the new module) ![Xcode Choose Package Products dialog with PushwooshBridge, PushwooshCore, PushwooshFramework, and PushwooshInboxKit added to the MyApp target](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-spm-products.webp) Add the four highlighted products to your main app target. 5. Open your main app target and under **Frameworks, Libraries, and Embedded Content**, confirm that `PushwooshInboxKit.xcframework` is listed and embedded. That’s it — InboxKit is now linked alongside the main SDK. ## Show the inbox Add the inbox controller to any navigation flow. The default configuration is enough to get a working inbox with the three standard cell types: * Swift ```swift import PushwooshInboxKit let inboxVC = PushwooshInboxKitViewController() navigationController?.pushViewController(inboxVC, animated: true) ``` * Objective-C ```objective-c @import PushwooshInboxKit; PushwooshInboxKitViewController *inboxVC = [PushwooshInboxKitViewController new]; [self.navigationController pushViewController:inboxVC animated:YES]; ``` ## Customize the inbox In Swift, configure the controller through the `PushwooshInboxKitAttributes` value type. In Objective-C, use the `@objc`-friendly setters on the controller — `PushwooshInboxKitAttributes` is a Swift struct and is not bridged. * Swift ```swift var attributes = PushwooshInboxKitAttributes() attributes.pullToRefreshEnabled = true attributes.swipeToDeleteEnabled = true attributes.pinningEnabled = true attributes.style.unreadBadgeColor = .systemBlue attributes.style.titleFont = .systemFont(ofSize: 17, weight: .semibold) let inboxVC = PushwooshInboxKitViewController(attributes: attributes) ``` * Objective-C ```objective-c PushwooshInboxKitViewController *inboxVC = [PushwooshInboxKitViewController new]; [inboxVC setBackgroundColor:[UIColor systemBackgroundColor]]; [inboxVC setEmptyMessage:@"You have no messages yet"]; ``` The `Style` struct exposes all colors, fonts, corner radii and the date formatter used by the default cells. Every value is an Apple semantic color by default, so the inbox reacts to system dark mode automatically. ![Custom-styled InboxKit feed with brand colors applied to title and unread indicator](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxkit-ios-custom.webp) Captioned cell with a custom theme applied through `PushwooshInboxKitAttributes.Style`. ## Handle taps and refreshes Conform to `PushwooshInboxKitDelegate` to react to user actions and refresh events. Every method has a default implementation, so you only override what you need: * Swift ```swift final class InboxCoordinator: NSObject, PushwooshInboxKitDelegate { func inboxKit(_ vc: PushwooshInboxKitViewController, didSelect message: PWInboxMessageProtocol) -> Bool { // Return true to let the SDK open the message URL or richmedia. // Return false if you handled the tap entirely (e.g. routed to a custom screen). return true } func inboxKit(_ vc: PushwooshInboxKitViewController, didRefreshWith messages: [PWInboxMessageProtocol], error: Error?) { // Show your own empty / error state here if needed. } } inboxVC.delegate = inboxCoordinator ``` Persistence Mark-as-read, mark-all-as-read, delete, and clear-read all persist to the local Pushwoosh inbox storage before the network request is sent. State survives a process restart even when the backend has not yet acknowledged the change. ## Next steps [PushwooshInboxKit API reference ](https://pushwoosh.github.io/pushwoosh-ios-sdk/PushwooshInboxKit/documentation/pushwooshinboxkit/)Generated DocC documentation for every public type. --- # Setting up Pushwoosh InboxUI iOS (legacy) **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/ Legacy Objective-C UIKit inbox UI for Pushwoosh iOS SDK. New projects should use Pushwoosh InboxKit instead for active development and full feature support. --- Legacy module — use InboxKit for new projects Pushwoosh InboxUI is the older Objective-C UIKit inbox layer. It is kept for backward compatibility, but new development happens in **Pushwoosh InboxKit** — a modern Swift UIKit module with banner / captioned / classic cards, inline CTA buttons, pinning, and code-driven cell customization. If you are starting a new integration, see [Setting up Pushwoosh InboxKit iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/) instead. This section provides step-by-step instructions for integrating Pushwoosh InboxUI using the method of your choice. ### Set up InboxUI with Swift Package Manager [Set up InboxUI with Swift Package Manager](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/set-up-inboxui-with-swift-package-manager/) ### Set up InboxUI with CocoaPods [Set up InboxUI with CocoaPods](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/set-up-inboxui-with-cocoapods/) --- # Set up InboxUI with CocoaPods **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/set-up-inboxui-with-cocoapods/ How to implement Pushwoosh InboxUI into your iOS app with CocoaPods. Legacy module — new projects should use InboxKit. --- Legacy module Pushwoosh InboxUI is kept for backward compatibility. For new projects, use [Pushwoosh InboxKit](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/) — the modern UIKit replacement. 1. Run ‘**pod init**’ command from the terminal in your project directory. 2. Open the newly created Podfile. 3. Add Pushwoosh dependency under your project name target: ```ruby # Uncomment the next line to define a global platform for your project # platform: ios, ‘9.0’ target ‘MyApp’ do # Comment the next line if you don’t want to use dynamic frameworks use_frameworks! # Pods for MyApp pod 'PushwooshInboxUI' end ``` Note If you want to use *PushwooshInboxUI.xcframework,* use **pod ‘PushwooshInboxUIXCFramework’** in your Podfile. **Catalyst integration** Catalyst uses .xcframework, so you should use **PushwooshXCFramework** pod instead of **Pushwoosh**: ```plaintext pod 'PushwooshXCFramework' ``` 4. Run the following command in your terminal in your project directory: ```plaintext pod install ``` 5. Open the newly created <*your_project_name*>**.xcworkspace** file. 6. Well done! You’ve just integrated Pushwoosh InboxUI to your project. ## Add Pushwoosh InboxUI code To show Inbox UI in your app, add Inbox view controller: * Swift ```swift self.navigationController?.pushViewController(PWIInboxUI.createInboxController(with: PWIInboxStyle.default()), animated: true) ``` * Objective-C ```objective-c [self.navigationController pushViewController:[PWIInboxUI createInboxControllerWithStyle:[PWIInboxStyle defaultStyle]] animated:YES]; ``` To make the Inbox match your app’s appearance, change the parameters of `PWIInboxStyle`. You can customize such parameters as font, background color, etc. * Swift ```swift //creating a new Inbox style let inboxStyle = PWIInboxStyle.customStyle(withDefaultImageIcon: UIImage.init(named: "custom_image"), textColor: UIColor.darkText, accentColor: UIColor.blue, font: UIFont.systemFont(ofSize: 17)) inboxStyle?.backgroundColor = UIColor.init(white: 1, alpha: 1) inboxStyle?.listErrorMessage = NSLocalizedString("Custom error message", comment: "Custom error message") inboxStyle?.listEmptyMessage = NSLocalizedString("Custom empty message", comment: "Custom empty message") PWIInboxStyle.setupDefaultStyle(inboxStyle) ``` * Objective-C ```objective-c //creating a new Inbox style PWIInboxStyle *inboxStyle = [PWIInboxStyle customStyleWithDefaultImageIcon:[UIImage imageNamed:@"custom_image"] textColor:UIColor.darkTextColor accentColor:UIColor.blueColor font:[UIFont systemFontOfSize:17]]; inboxStyle.backgroundColor = [UIColor colorWithWhite:1 alpha:1]; inboxStyle.listErrorMessage = NSLocalizedString(@"Custom error message", @"Custom error message"); inboxStyle.listEmptyMessage = NSLocalizedString(@"Custom empty message", @"Custom empty message"); [PWIInboxStyle setupDefaultStyle:inboxStyle]; ``` ## Customization To customize Inbox UI style, please refer to: [PWIInboxStyle.h ](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/iOS_SDK/PushwooshInboxUI/PushwooshInboxUI/Public/PWIInboxStyle.h) [PWIInboxUI.h ](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/iOS_SDK/PushwooshInboxUI/PushwooshInboxUI/Public/PWIInboxUI.h) For more details, take a look at [Inbox Sample](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/inbox). --- # Set up InboxUI with Swift Package Manager **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxui-ios/set-up-inboxui-with-swift-package-manager/ Pushwoosh Inbox UI iOS setup with Swift Package Manager. Legacy module — new projects should use InboxKit. --- Legacy module Pushwoosh InboxUI is kept for backward compatibility. For new projects, use [Pushwoosh InboxKit](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-inboxkit-ios/) — the modern UIKit replacement. 1. Open your project in Xcode and Navigate to the project’s settings -> Package Dependencies, then press ’+’ button. ![Package Dependencies](https://docs.pushwoosh.com/setting-up-pushwoosh-inboxui-ios-set-up-inboxui-with-swift-package-manager-1.webp) 2. Enter the following Package URL: ```bash https://github.com/Pushwoosh/PushwooshInboxUI-XCFramework ``` 3. Set up the ‘Dependency Rule’ to **Branch** and **main**. Then, click ‘**Add Package**’ button. 4. On the next screen to choose your Package, select the Pushwoosh Package and **Add Package** to your main app Target. 5. Open your Main App Target and under “Frameworks, Libraries, and Embedded Content” ensure that the PushwooshInboxUI has been added. 6. That’s it! You’ve just integrated Pushwoosh InboxUI SDK to your project. ## Add Pushwoosh InboxUI code To show Inbox UI in your app, add Inbox view controller: * Swift ```swift self.navigationController?.pushViewController(PWIInboxUI.createInboxController(with: PWIInboxStyle.default()), animated: true) ``` * Objective-C ```objective-c [self.navigationController pushViewController:[PWIInboxUI createInboxControllerWithStyle:[PWIInboxStyle defaultStyle]] animated:YES]; ``` To make the Inbox match your app’s appearance, change the parameters of `PWIInboxStyle`. You can customize such parameters as font, background color, etc. * Swift ```swift //creating a new Inbox style let inboxStyle = PWIInboxStyle.customStyle(withDefaultImageIcon: UIImage.init(named: "custom_image"), textColor: UIColor.darkText, accentColor: UIColor.blue, font: UIFont.systemFont(ofSize: 17)) inboxStyle?.backgroundColor = UIColor.init(white: 1, alpha: 1) inboxStyle?.listErrorMessage = NSLocalizedString("Custom error message", comment: "Custom error message") inboxStyle?.listEmptyMessage = NSLocalizedString("Custom empty message", comment: "Custom empty message") PWIInboxStyle.setupDefaultStyle(inboxStyle) ``` * Objective-C ```objective-c //creating a new Inbox style PWIInboxStyle *inboxStyle = [PWIInboxStyle customStyleWithDefaultImageIcon:[UIImage imageNamed:@"custom_image"] textColor:UIColor.darkTextColor accentColor:UIColor.blueColor font:[UIFont systemFontOfSize:17]]; inboxStyle.backgroundColor = [UIColor colorWithWhite:1 alpha:1]; inboxStyle.listErrorMessage = NSLocalizedString(@"Custom error message", @"Custom error message"); inboxStyle.listEmptyMessage = NSLocalizedString(@"Custom empty message", @"Custom empty message"); [PWIInboxStyle setupDefaultStyle:inboxStyle]; ``` ## Customization To customize Inbox UI style, please refer to: [PWIInboxStyle.h ](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/iOS_SDK/PushwooshInboxUI/PushwooshInboxUI/Public/PWIInboxStyle.h) [PWIInboxUI.h ](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/iOS_SDK/PushwooshInboxUI/PushwooshInboxUI/Public/PWIInboxUI.h) For more details, take a look at [Inbox Sample](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/tree/main/inbox). --- # iOS SDK 7.0+ advanced integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/advanced-integration-guide/ This section provides information on advanced integration of the Pushwoosh iOS SDK 7.0+. --- This section provides information on advanced integration of the Pushwoosh iOS SDK. ## Background modes Caution By default, iOS does not allow apps to process push notifications when they are in the background. This includes silent push notifications, which are useful for updating app data without user interaction. To enable this functionality, you must add Background Modes to your project. #### Steps to enable background modes 1. Open your project in **Xcode** and select it in the **Project Navigator**. 2. Choose your app target from the left panel. 3. Navigate to the **Signing & Capabilities** tab. 4. Click the **+ Capability** button in the upper-left corner. 5. Search for and select **Background Modes** from the list. 6. In the **Background Modes** section, enable **Remote notifications** by checking the box. Once completed, your app will be able to handle push notifications, including silent ones, while running in the background. ## Foreground modes By default, Pushwoosh iOS SDK displays the notification banner when the app is running in the foreground. You can control this behavior by setting the following boolean flag in your code (i.e., in your `AppDelegate`): * Swift ```swift // Set false to disable foreground notifications, true to enable it Pushwoosh.configure.showPushnotificationAlert = true ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/ViewController.swift#L30) * Objective-C ```objective-c // Set 0 to disable foreground notifications, 1 to enable it [[Pushwoosh configure] setShowPushnotificationAlert:0]; ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/ViewController.m#L35) ## Log level The Pushwoosh iOS SDK supports the following logging levels: * `NONE` - No logs from SDK. * `ERROR` - Displays only error messages in the console. * `WARNING` - Displays warnings in addition to errors. * `INFO` - Includes informational messages (default setting). * `DEBUG` - Includes detailed debug information. By default, the logging level is set to INFO, ensuring the SDK provides relevant information without cluttering the developer console. To modify the logging level, update the `Pushwoosh_LOG_LEVEL` key in your app’s `Info.plist` file: ```xml Pushwoosh_LOG_LEVEL YOUR_LOG_LEVEL ``` Alternatively, you can change the log level using the code snippet below: ```swift Pushwoosh.Debug.setLogLevel(.PW_LL_DEBUG) ``` Replace `YOUR_LOG_LEVEL` with the desired level (e.g., `DEBUG` or `ERROR`). ## Custom `UNNotificationCenterDelegate` If you want to use your own `UNNotificationCenterDelegate` (for example, for local notifications), you should inform Pushwoosh SDK about it for proper behavior. You can do it with the `addNotificationCenterDelegate` method: * Swift ```swift Pushwoosh.configure.addNotificationCenterDelegate(my_delegate) ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/Custom%20UNNotificationCenterDelegate/CustomNotificationCDViewConrtoller.swift#L23) * Objective-C ```objective-c [Pushwoosh.configure addNotificationCenterDelegate:my_delegate]; ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Custom%20UNNotificationCenterDelegate/PWCustomNotificationCDViewConrtoller.m#L28) Then, implement `UNNotificationCenterDelegate` methods in your delegate: * Swift ```swift func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { if (!PWMessage.isPushwooshMessage(notification.request.content.userInfo)) { // Handle your notification completionHandler(UNNotificationPresentationOptions.alert) } } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { if (!PWMessage.isPushwooshMessage(response.notification.request.content.userInfo)) { // Handle your notification completionHandler() } } ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/Custom%20UNNotificationCenterDelegate/CustomNotificationCDViewConrtoller.swift) * Objective-C ```objective-c - (void)userNotificationCenter:(UNNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (![PWMessage isPushwooshMessage:notification.request.content.userInfo]) { // Handle your message completionHandler(UNNotificationPresentationOptionAlert); } } - (void)userNotificationCenter:(UNNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { if (![PWMessage.isPushwooshMessage:response.notification.request.content.userInfo]) { // Handle your message completionHandler(); } } ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Custom%20UNNotificationCenterDelegate/PWCustomNotificationCDViewConrtoller.m) ## Pushwoosh Lazy Initialization The `Pushwoosh_LAZY_INITIALIZATION` flag prevents the automatic initialization of the Pushwoosh SDK when the application starts. This allows for more control over when the Pushwoosh SDK services are started. When this flag is enabled, the Pushwoosh SDK does not start its services until Pushwoosh iOS SDK methods are explicitly called. Add the following entry to Info.plist: ```xml Pushwoosh_LAZY_INITIALIZATION ``` **Use Cases** 1. **Controlled SDK Initialization** – The Pushwoosh_LAZY_INITIALIZATION flag allows delaying the Pushwoosh SDK startup, giving more control over when push services are activated. 2. **Deferred Push Activation** – In some applications, push notifications should only be initialized under specific conditions. Enabling this flag ensures that the Pushwoosh SDK starts only when explicitly requested. 3. **User-Specific Push Configuration** – Some applications may require customizing push notification settings based on user preferences or account settings. With lazy initialization, the Pushwoosh SDK starts only after the appropriate configuration is determined. ## Complete list of Info.plist properties | Property | Description | Possible Values | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `Pushwoosh_APPID` | Sets the Pushwoosh application ID for production build. | `XXXXX-XXXXX` **Type**: String | | `Pushwoosh_APPID_Dev` | Sets the Pushwoosh application ID for development build. | `XXXXX-XXXXX` **Type**: String | | `Pushwoosh_SHOW_ALERT` | Shows notification foreground alert. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALERT_TYPE` | Sets the notification alert style. | `BANNER` *(default)* / `ALERT` / `NONE` **Type**: String | | `Pushwoosh_BASEURL` | Overrides the Pushwoosh server base URL. | [`https://cp.pushwoosh.com/json/1.3/`](https://cp.pushwoosh.com/json/1.3/) *(default)* **Type**: String | | `Pushwoosh_AUTO_ACCEPT_DEEP_LINK_FOR_SILENT_PUSH` | If `YES`, Deep Links received in silent pushes will be processed automatically. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_SERVER_COMMUNICATION` | Allows the SDK to send network requests to Pushwoosh servers. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_DATA` | Allows the SDK to collect and send device data (OS version, locale, and model) to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_OS_VERSION` | Allows the SDK to collect and send the device’s OS version to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_LOCALE` | Allows the SDK to collect and send the device locale to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_MODEL` | Allows the SDK to collect and send the device model to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_SCREEN_NAME` | Allows the SDK to attach the current screen name to `PW_ScreenOpen`, `PW_UserIdle`, and `PW_ApplicationExit` events. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_LOG_LEVEL` | Pushwoosh SDK logging level. For details, refer to [Controlling Log Level](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/advanced-integration-guide/#log-level). | `NONE` / `ERROR` / `WARNING` / `INFO` *(default)* / `DEBUG` / `VERBOSE` **Type**: String | | `Pushwoosh_PURCHASE_TRACKING_ENABLED` | Allows the SDK to track in-app purchases. Needed for Customer Journey Builder. | `YES` / `NO` *(default)* **Type**: Boolean | On platforms that can’t edit Info.plist (Cordova, OutSystems), use the runtime equivalent instead: `Pushwoosh.configure.setAllowCollectingScreenName:`. --- # iOS SDK 7.0+ basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/ Learn how to integrate the Pushwoosh iOS SDK 7.0+, configure users, track events, set tags, and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh SDK into your iOS application. ## Prerequisites To integrate the Pushwoosh iOS SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Installation You can integrate the Pushwoosh SDK into your application using either **Swift Package Manager** or **CocoaPods**. #### Swift Package Manager In the **Package Dependencies** section, add the following package: ```plaintext https://github.com/Pushwoosh/Pushwoosh-XCFramework ``` To use the Pushwoosh iOS SDK, make sure to add the following three frameworks to your app target when integrating via Swift Package Manager: * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` #### CocoaPods Open your `Podfile` and add the dependency: ```bash # Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'PushwooshXCFramework' end ``` Then, in the terminal, run the following command to install dependencies: ```bash pod install ``` ### 2. Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. ### 3. Initialization code #### AppDelegate Add the following code to your AppDelegate class: * SwiftUI ```swift import SwiftUI import PushwooshFramework @main struct MyApp: App { // Register AppDelegate as UIApplicationDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Initialization code // Set custom delegate for push handling Pushwoosh.configure.delegate = self // Register for push notifications Pushwoosh.configure.registerForPushNotifications() return true } // Handle token received from APNS func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.configure.handlePushRegistration(deviceToken) } // Handle token receiving error func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.configure.handlePushRegistrationFailure(error) } //for silent push notifications func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.configure.handlePushReceived(userInfo) completionHandler(.noData) } // Fired when a push is received func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { print("onMessageReceived: ", message.payload!.description) } // Fired when a user taps the notification func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("onMessageOpened: ", message.payload!.description) } } struct ContentView: View { var body: some View { Text("Pushwoosh with SwiftUI") .padding() } } ``` * Swift ```swift import PushwooshFramework @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, PWMessagingDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { //initialization code //set custom delegate for push handling, in our case AppDelegate Pushwoosh.configure.delegate = self; //register for push notifications Pushwoosh.configure.registerForPushNotifications() return true } //handle token received from APNS func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.configure.handlePushRegistration(deviceToken) } //handle token receiving error func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.configure.handlePushRegistrationFailure(error); } //for silent push notifications func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.configure.handlePushReceived(userInfo) completionHandler(.noData) } //this event is fired when the push gets received func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { print("onMessageReceived: ", message.payload!.description) } // Fired when a user taps the notification func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("onMessageOpened: ", message.payload!.description) } } ``` * Objective-C ```objective-c #import @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //-----------PUSHWOOSH PART----------- // set custom delegate for push handling, in our case AppDelegate [[Pushwoosh configure] setDelegate:self]; //register for push notifications! [[Pushwoosh configure] registerForPushNotifications]; return YES; } //handle token received from APNS - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [[Pushwoosh configure] handlePushRegistration:deviceToken]; } //handle token receiving error - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [[Pushwoosh configure] handlePushRegistrationFailure:error]; } //for silent push notifications - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [[Pushwoosh configure] handlePushReceived:userInfo]; completionHandler(UIBackgroundFetchResultNoData); } //this event is fired when the push gets received - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageReceived:(PWMessage *)message { NSLog(@"onMessageReceived: %@", message.payload); } //this event is fired when user taps the notification - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageOpened:(PWMessage *)message { NSLog(@"onMessageOpened: %@", message.payload); } @end ``` #### Info.plist In your `Info.plist`: * set the `Pushwoosh_APPID` key to the Pushwoosh Application Code. * set the `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### 4. Message delivery tracking Pushwoosh supports tracking delivery events for push notifications via the Notification Service Extension #### Add Notification Service Extension 1. In Xcode, Select **File** > **New** > **Target…** 2. Choose **Notification Service Extension** and press **Next.** 3. Enter the target name and press **Finish.** 4. When prompted for activation, press **Cancel.** #### Dependencies for the Notification Service Extension (CocoaPods only) Note: If you’re using Swift Package Manager to manage dependencies, you can skip this step, as the dependencies are added automatically. Open your `Podfile` and add the dependency for the target: Podfile ```ruby # Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'PushwooshXCFramework' end target 'MyAppNotificationExtension' do use_frameworks! pod 'PushwooshXCFramework' end ``` Run the following command in the terminal to update the dependencies: ```shell pod update ``` #### Add Pushwoosh SDK to Notification Service Extension Replace the generated `NotificationService` class with a subclass of `PushwooshNotificationServiceExtension`. Pushwoosh then handles everything a push needs — sending the delivery event, badge counting, downloading the media attachment, and the mandatory `serviceExtensionTimeWillExpire` timeout fallback. No other code is required. * Swift ```swift import PushwooshFramework class NotificationService: PushwooshNotificationServiceExtension {} ``` * Objective-C ```objective-c #import @interface NotificationService : PushwooshNotificationServiceExtension @end @implementation NotificationService @end ``` Note: you can skip the source file entirely — set the extension’s `NSExtensionPrincipalClass` to `PushwooshNotificationServiceExtension` in its Info.plist and write no code at all. To modify the notification before it is shown, override `didReceive(_:withContentHandler:)`, call `super` with your own content handler, mutate the content inside it, then forward it to the original handler. Pushwoosh still runs the delivery event, badge, attachment, and timeout fallback. * Swift ```swift import UserNotifications import PushwooshFramework class NotificationService: PushwooshNotificationServiceExtension { override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { super.didReceive(request) { content in let mutable = (content.mutableCopy() as? UNMutableNotificationContent) ?? content // Modify the notification content here... contentHandler(mutable) } } } ``` * Objective-C ```objective-c #import @interface NotificationService : PushwooshNotificationServiceExtension @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { [super didReceiveNotificationRequest:request withContentHandler:^(UNNotificationContent *content) { UNMutableNotificationContent *mutable = [content mutableCopy]; // Modify the notification content here... contentHandler(mutable); }]; } @end ``` #### Info.plist The extension inherits `Pushwoosh_APPID` (and other `Pushwoosh_*` keys) from the host app, so you don’t need to duplicate them in the extension’s Info.plist. Add a key there only when you want to override the host value. To sync the badge count and reverse-proxy settings with the app, share an [App Group](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-badges) between the app and the extension. Add the App Groups capability to both targets, then set the App Group ID in the **main app’s** Info.plist: * `PW_APP_GROUPS_NAME` - your App Group identifier (for example, `group.com.example.app`). The extension inherits this value from the host app, so you don’t need to repeat it in the extension’s Info.plist — add it there only to override the host. Alternatively, provide it in code by overriding `pushwooshAppGroupsName`. ### 5. Run the project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended Pushwoosh iOS integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notifications In the Pushwoosh SDK, there are two callbacks designed for handling push notifications: * `onMessageReceived`: This method is invoked when a push notification is received. * `onMessageOpened`: This method is called when the user interacts with (opens) the notification These callbacks enable developers to manage the reception and user interaction with push notifications within their applications * Swift ```swift import PushwooshFramework class AppDelegate: NSObject, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Pushwoosh.configure.delegate = self; } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { if let payload = message.payload { print("onMessageOpened: \(payload)") } } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { if let payload = message.payload { print("onMessageReceived: \(payload)") } } } ``` * Objective-C ```objective-c #import @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[Pushwoosh configure] setDelegate:self]; return YES; } - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageOpened:(PWMessage *)message { if (message.payload) { NSLog(@"onMessageOpened: %@", message.payload); } } - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageReceived:(PWMessage *)message { if (message.payload) { NSLog(@"onMessageReceived: %@", message.payload); } } @end ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty * Swift ```swift import PushwooshFramework class Registration { func afterUserLogin(user: User) { let pushwoosh = Pushwoosh.configure // set user ID if let userId = user.userId { pushwoosh.setUserId(userId) } // set user email if let userEmail = user.email { pushwoosh.setEmail(userEmail) } // set user SMS number if let userSmsNumber = user.SmsNumber { pushwoosh.registerSmsNumber(userSmsNumber) } // set user WhatsApp number if let userWhatsAppNumber = user.WhatsAppNumber { pushwoosh.registerSmsNumber(userWhatsAppNumber) } // setting additional user information as tags for Pushwoosh if let age = user.userDetails.age, let name = user.userDetails.userName, let lastLogin = user.userDetails.lastLoginDate { pushwoosh.setTags([ "age": age, "name": name, "last_login": lastLogin ]) } } } ``` * Objective-C ```objective-c #import @implementation Registration - (void)afterUserLogin:(User *)user { Pushwoosh *pushwoosh = [Pushwoosh configure]; // set user ID if (user.userId) { [pushwoosh setUserId:user.userId]; } // set user email if (user.email) { [pushwoosh setEmail:user.email]; } // setting additional user information as tags for Pushwoosh if (user.userDetails.age && user.userDetails.userName && user.userDetails.lastLoginDate) { NSDictionary *tags = @{ @"age": user.userDetails.age, @"name": user.userDetails.userName, @"last_login": user.userDetails.lastLoginDate }; [pushwoosh setTags:tags]; } } @end ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. * Swift ```swift import PushwooshFramework class UpdateUser { func afterUserUpdateProfile(user: User) { let pushwoosh = Pushwoosh.configure // set list of favorite categories pushwoosh.setTags(["favorite_categories" : user.getFavoriteCategories()]) // set payment information pushwoosh.setTags([ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() ]) } } ``` * Objective-C ```objective-c #import @implementation UpdateUser - (void)afterUserUpdateProfile:(User *)user { Pushwoosh *pushwoosh = [Pushwoosh configure]; // set list of favorite categories [pushwoosh setTags:@{@"favorite_categories" : user.getFavoriteCategories}]; // set payment information NSDictionary *tags = @{ @"is_subscribed": @(user.isSubscribed), @"payment_status": user.getPaymentStatus, @"billing_address": user.getBillingAddress }; [pushwoosh setTags:tags]; } @end ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions * Swift ```swift import PushwooshFramework class Registration { func afterUserLogin(user: User) { if let userName = user.getUserName(), let lastLogin = user.getLastLoginDate() { PWInAppManager.shared().postEvent("login", withAttributes: [ "name": userName, "last_login": lastLogin ]) } } func afterUserPurchase(user: User, product: Product) { let pushwoosh = Pushwoosh.configure // Track purchase event PWInAppManager.shared().postEvent("purchase", withAttributes: [ "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() ]) // Set user tags let lastPurchaseDate = Date().timeIntervalSince1970 let lifetimeSpend = getCurrentLifetimeSpend() + product.getPrice() pushwoosh.setTags([ "last_purchase_date": lastPurchaseDate, "lifetime_spend": lifetimeSpend ]) } } ``` * Objective-C ```objective-c #import #import @implementation Registration - (void)afterUserLogin:(User *)user { NSString *userName = [user getUserName]; NSDate *lastLogin = [user getLastLoginDate]; if (userName && lastLogin) { [[PWInAppManager sharedManager] postEvent:@"login" withAttributes:@{ @"name": userName, @"last_login": lastLogin }]; } } - (void)afterUserPurchase:(User *)user product:(Product *)product { Pushwoosh *pushwoosh = [Pushwoosh configure]; // Track purchase event [[PWInAppManager sharedManager] postEvent:@"purchase" withAttributes:@{ @"product_id": [product getId], @"product_name": [product getName], @"price": @([product getPrice]), @"quantity": @([product getQuantity]) }]; // Set user tags NSTimeInterval lastPurchaseDate = [[NSDate date] timeIntervalSince1970]; double lifetimeSpend = /* fetch current lifetime spend */ + [product getPrice]; NSDictionary *tags = @{ @"last_purchase_date": @(lastPurchaseDate), @"lifetime_spend": @(lifetimeSpend) }; [pushwoosh setTags:tags]; } @end ``` ### Rich Media Rich media refers to interactive and multimedia content, such as images, videos, or HTML, used in notifications and in-app messages to enhance user engagement * Swift ```swift import PushwooshFramework class ViewController: UIViewController, PWRichMediaPresentingDelegate { override func viewDidLoad() { super.viewDidLoad() let richMediaConfiguration = PWModalWindowConfiguration.shared() PWRichMediaManager.shared().delegate = self richMediaConfiguration.configureModalWindow(with: .PWModalWindowPositionBottom, present: .PWAnimationPresentFromBottom, dismiss: .PWAnimationDismissDown) } func richMediaManager(_ richMediaManager: PWRichMediaManager!, shouldPresent richMedia: PWRichMedia!) -> Bool { print("Rich media will be presented with: \(richMedia.pushPayload!)") return true } func richMediaManager(_ richMediaManager: PWRichMediaManager!, didPresent richMedia: PWRichMedia!) { print("Rich media has been presented with: \(richMedia.pushPayload!)") } func richMediaManager(_ richMediaManager: PWRichMediaManager!, didClose richMedia: PWRichMedia!) { print("Rich media has been closed with: \(richMedia.pushPayload!)") } func richMediaManager(_ richMediaManager: PWRichMediaManager!, presentingDidFailFor richMedia: PWRichMedia!, withError error: (any Error)!) { print("Failed to present rich media with: \(richMedia.pushPayload!). Error: \(error.localizedDescription)") } } ``` * Objective-C ```objective-c #import "ViewController.h" #import #import #import @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[PWRichMediaManager sharedManager] setDelegate:self]; [[PWModalWindowConfiguration shared] configureModalWindowWith:PWModalWindowPositionBottom presentAnimation:PWAnimationPresentFromBottom dismissAnimation:PWAnimationDismissDown]; } - (BOOL)richMediaManager:(PWRichMediaManager *)richMediaManager shouldPresentRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media will be presented with: %@", richMedia.pushPayload); return YES; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didPresentRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media has been presented with: %@", richMedia.pushPayload); } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didCloseRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media has been closed with:: %@", richMedia.pushPayload); } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager presentingDidFailForRichMedia:(PWRichMedia *)richMedia withError:(NSError *)error { NSLog(@"Failed to present rich media with: %@. Error: %@", richMedia.pushPayload, error.localizedDescription); } @end ``` ## Troubleshooting ### Failed to build module ‘PushwooshFramework’ When building your project, you may encounter an error similar to: ```plaintext Failed to build module 'PushwooshFramework'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)', while this compiler is 'Apple Swift version 6.1.2 effective-5.10 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)') ``` **Cause:** This error is not related to Swift compiler version incompatibility. Starting from Pushwoosh iOS SDK version 6.8.0, the SDK is modularized into several components that interact with each other. The error occurs when not all required frameworks are added to your project. **Solution:** Ensure that all four required frameworks are added to your app target when integrating via Swift Package Manager: * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` * `PushwooshLiveActivities` To verify this in Xcode: 1. Select your project in the Project Navigator 2. Select your app target 3. Go to **General** > **Frameworks, Libraries, and Embedded Content** 4. Confirm all four frameworks are listed ### Push sent but not delivered If a push isn’t delivered to iOS devices, see [iOS, Mac OS X, Safari errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/ios-os-x-safari-errors/) for the error codes and how to fix each one. *** If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # iOS SDK 7.0+ quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/quick-start/ Get started with the Pushwoosh iOS SDK 7.0+ using this quick and simple example --- Get started with the Pushwoosh iOS SDK using this quick and simple example. ## What’s new in iOS SDK 7.0+ Starting with version 7.0, the Pushwoosh iOS SDK has been redesigned with a fully modular architecture. The SDK is now split into separate modules: **PushwooshFramework**, **PushwooshCore**, **PushwooshBridge**, and **PushwooshLiveActivities** are required for basic integration, while **PushwooshForegroundPush**, **PushwooshVoIP**, and **PushwooshTVOS** can be added optionally based on your needs. This modular approach allows you to include only the features your app actually uses, reducing the final binary size and improving build times. Each module can be updated independently, making it easier to adopt new features without affecting the rest of your integration. This release introduces native Swift Package Manager support for easier dependency management, along with improved Swift interoperability across all modules. Documentation is now embedded directly into the binary frameworks, so you can access API reference and code examples right in Xcode via Quick Help and the Developer Documentation window. New features include full support for iOS 16+ Live Activities with Dynamic Island integration, customizable foreground push notifications with animated banners and haptic feedback, enhanced VoIP capabilities, and a dedicated tvOS module for Apple TV applications. ## Prerequisites To integrate the Pushwoosh iOS SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project Clone the example project from GitHub: ```bash git clone https://github.com/Pushwoosh/pushwoosh-ios-sample.git ``` ## Configure the project 1. Open the project in Xcode. 2. Add `https://github.com/Pushwoosh/Pushwoosh-XCFramework` as Swift Package Dependency. 3. Set the **bundle identifier** for the main target (PushwooshSampleApp) to match your Pushwoosh project (e.g., `com.pushwoosh.PushwooshSampleApp`). 4. Use the same **bundle identifier** for the NotificationService target, appending `.NotificationService` (e.g., `com.pushwoosh.PushwooshSampleApp.NotificationService`). 5. In `Info.plist`, set the: * `Pushwoosh_APPID` key to the Pushwoosh Application Code * `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ## Run the project 1. Build and run the project. 2. The app will have two tabs: “Actions” and “Settings”. Navigate to the **Settings** tab. 3. Tap the **Register for Pushes** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. You should see a log entry like this: ```bash Pushwoosh: Initializing application runtime [PW] BUNDLE ID: __YOUR_BUNDLE_ID__ [PW] APP CODE: __YOUR_APP_CODE__ [PW] PUSHWOOSH SDK VERSION: 7.0.0 [PW] HWID: __YOUR_HWID__ [PW] PUSH TOKEN: (null) [PW] [I] -[PWNotificationManagerCompatiOS10] NotificationCenter authorization granted: 1 [PW] [I] -[PWPushNotificationsManagerCommon] Registered for push notifications: __YOUR_PUSH_TOKEN__ ``` Your device is now registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. ## Migrating from SDK 6.x If you’re upgrading from SDK version 6.x, the main change is how you access the Pushwoosh instance. In version 7.0+, use `Pushwoosh.configure` instead of `Pushwoosh.sharedInstance()`: **Before (6.x):** ```swift Pushwoosh.sharedInstance().registerForPushNotifications() Pushwoosh.sharedInstance().delegate = self ``` **After (7.0+):** ```swift Pushwoosh.configure.registerForPushNotifications() Pushwoosh.configure.delegate = self ``` All other APIs remain the same, so updating your code requires only replacing `sharedInstance()` with `configure` throughout your project. ## What’s next To learn more, please refer to the [basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/) --- # iOS SDK advanced integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/advanced-integration-guide/ This section provides information on advanced integration of the Pushwoosh iOS SDK. --- This section provides information on advanced integration of the Pushwoosh iOS SDK. ## Background modes Caution By default, iOS does not allow apps to process push notifications when they are in the background. This includes silent push notifications, which are useful for updating app data without user interaction. To enable this functionality, you must add Background Modes to your project. #### Steps to enable background modes 1. Open your project in **Xcode** and select it in the **Project Navigator**. 2. Choose your app target from the left panel. 3. Navigate to the **Signing & Capabilities** tab. 4. Click the **+ Capability** button in the upper-left corner. 5. Search for and select **Background Modes** from the list. 6. In the **Background Modes** section, enable **Remote notifications** by checking the box. Once completed, your app will be able to handle push notifications, including silent ones, while running in the background. ## Foreground modes By default, Pushwoosh iOS SDK displays the notification banner when the app is running in the foreground. You can control this behavior by setting the following boolean flag in your code (i.e., in your `AppDelegate`): * Swift ```swift // Set false to disable foreground notifications, true to enable it Pushwoosh.sharedInstance().showPushnotificationAlert = true ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/ViewController.swift#L30) * Objective-C ```objective-c // Set 0 to disable foreground notifications, 1 to enable it [[Pushwoosh sharedInstance] setShowPushnotificationAlert:0]; ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/ViewController.m#L35) ## Log level The Pushwoosh iOS SDK supports the following logging levels: * `NONE` - No logs from SDK. * `ERROR` - Displays only error messages in the console. * `WARNING` - Displays warnings in addition to errors. * `INFO` - Includes informational messages (default setting). * `DEBUG` - Includes detailed debug information. By default, the logging level is set to INFO, ensuring the SDK provides relevant information without cluttering the developer console. To modify the logging level, update the `Pushwoosh_LOG_LEVEL` key in your app’s `Info.plist` file: ```xml Pushwoosh_LOG_LEVEL YOUR_LOG_LEVEL ``` Alternatively, you can change the log level using the code snippet below: ```swift Pushwoosh.Debug.setLogLevel(.PW_LL_DEBUG) ``` Replace `YOUR_LOG_LEVEL` with the desired level (e.g., `DEBUG` or `ERROR`). ## Custom `UNNotificationCenterDelegate` If you want to use your own `UNNotificationCenterDelegate` (for example, for local notifications), you should inform Pushwoosh SDK about it for proper behavior. You can do it with the `notificationCenterDelegateProxy` property of the Pushwoosh instance: * Swift ```swift Pushwoosh.sharedInstance().notificationCenterDelegateProxy?.add(my_delegate) ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/Custom%20UNNotificationCenterDelegate/CustomNotificationCDViewConrtoller.swift#L23) * Objective-C ```objective-c [Pushwoosh.sharedInstance.notificationCenterDelegateProxy addNotificationCenterDelegate:my_delegate]; ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Custom%20UNNotificationCenterDelegate/PWCustomNotificationCDViewConrtoller.m#L28) Then, implement `UNNotificationCenterDelegate` methods in your delegate: * Swift ```swift func userNotificationCenter( _ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void ) { if (!PWMessage.isPushwooshMessage(notification.request.content.userInfo)) { // Handle your notification completionHandler(UNNotificationPresentationOptions.alert) } } func userNotificationCenter( _ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void ) { if (!PWMessage.isPushwooshMessage(response.notification.request.content.userInfo)) { // Handle your notification completionHandler() } } ``` [Example (Swift) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizing/Custom%20UNNotificationCenterDelegate/CustomNotificationCDViewConrtoller.swift) * Objective-C ```objective-c - (void)userNotificationCenter:(UNNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler { if (![PWMessage isPushwooshMessage:notification.request.content.userInfo]) { // Handle your message completionHandler(UNNotificationPresentationOptionAlert); } } - (void)userNotificationCenter:(UNNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { if (![PWMessage.isPushwooshMessage:response.notification.request.content.userInfo]) { // Handle your message completionHandler(); } } ``` [Example (Objective-C) ](https://github.com/Pushwoosh/pushwoosh-quickstart-ios/blob/main/customizing/customizingObjC/customizingObjC/Custom%20UNNotificationCenterDelegate/PWCustomNotificationCDViewConrtoller.m) ## Pushwoosh Lazy Initialization The `Pushwoosh_LAZY_INITIALIZATION` flag prevents the automatic initialization of the Pushwoosh SDK when the application starts. This allows for more control over when the Pushwoosh SDK services are started. When this flag is enabled, the Pushwoosh SDK does not start its services until Pushwoosh iOS SDK methods are explicitly called. Add the following entry to Info.plist: ```xml Pushwoosh_LAZY_INITIALIZATION ``` **Use Cases** 1. **Controlled SDK Initialization** – The Pushwoosh_LAZY_INITIALIZATION flag allows delaying the Pushwoosh SDK startup, giving more control over when push services are activated. 2. **Deferred Push Activation** – In some applications, push notifications should only be initialized under specific conditions. Enabling this flag ensures that the Pushwoosh SDK starts only when explicitly requested. 3. **User-Specific Push Configuration** – Some applications may require customizing push notification settings based on user preferences or account settings. With lazy initialization, the Pushwoosh SDK starts only after the appropriate configuration is determined. ## Complete list of Info.plist properties | Property | Description | Possible Values | | ------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | `Pushwoosh_APPID` | Sets the Pushwoosh application ID for production build. | `XXXXX-XXXXX` **Type**: String | | `Pushwoosh_APPID_Dev` | Sets the Pushwoosh application ID for development build. | `XXXXX-XXXXX` **Type**: String | | `Pushwoosh_SHOW_ALERT` | Shows notification foreground alert. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALERT_TYPE` | Sets the notification alert style. | `BANNER` *(default)* / `ALERT` / `NONE` **Type**: String | | `Pushwoosh_BASEURL` | Overrides the Pushwoosh server base URL. | [`https://cp.pushwoosh.com/json/1.3/`](https://cp.pushwoosh.com/json/1.3/) *(default)* **Type**: String | | `Pushwoosh_AUTO_ACCEPT_DEEP_LINK_FOR_SILENT_PUSH` | If `YES`, Deep Links received in silent pushes will be processed automatically. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_SERVER_COMMUNICATION` | Allows the SDK to send network requests to Pushwoosh servers. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_DATA` | Allows the SDK to collect and send device data (OS version, locale, and model) to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_OS_VERSION` | Allows the SDK to collect and send the device’s OS version to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_LOCALE` | Allows the SDK to collect and send the device locale to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_DEVICE_MODEL` | Allows the SDK to collect and send the device model to the server. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_ALLOW_COLLECTING_SCREEN_NAME` | Allows the SDK to attach the current screen name to `PW_ScreenOpen`, `PW_UserIdle`, and `PW_ApplicationExit` events. | `YES` *(default)* / `NO` **Type**: Boolean | | `Pushwoosh_LOG_LEVEL` | Pushwoosh SDK logging level. For details, refer to [Controlling Log Level](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/advanced-integration-guide/#log-level). | `NONE` / `ERROR` / `WARNING` / `INFO` *(default)* / `DEBUG` / `VERBOSE` **Type**: String | | `Pushwoosh_PURCHASE_TRACKING_ENABLED` | Allows the SDK to track in-app purchases. Needed for Customer Journey Builder. | `YES` / `NO` *(default)* **Type**: Boolean | On platforms that can’t edit Info.plist (Cordova, OutSystems), use the runtime equivalent instead: `Pushwoosh.configure.setAllowCollectingScreenName:`. --- # iOS SDK basic integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/ Learn how to integrate the Pushwoosh iOS SDK, configure users, track events, set tags, and handle push notifications on iOS --- This section contains information on how to integrate the Pushwoosh SDK into your iOS application. ## Prerequisites To integrate the Pushwoosh iOS SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Integration steps ### 1. Installation You can integrate the Pushwoosh SDK into your application using either **Swift Package Manager** or **CocoaPods**. #### Swift Package Manager In the **Package Dependencies** section, add the following package: ```plaintext https://github.com/Pushwoosh/Pushwoosh-XCFramework ``` To use the Pushwoosh iOS SDK, make sure to add the following four frameworks to your app target when integrating via Swift Package Manager: * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` * `PushwooshLiveActivities` #### CocoaPods Open your `Podfile` and add the dependency: ```bash # Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'PushwooshXCFramework' end ``` Then, in the terminal, run the following command to install dependencies: ```bash pod install ``` ### 2. Capabilities To enable Push Notifications in your project, you need to add certain capabilities. In the Signing & Capabilities section, add the following capabilities: * `Push Notifications` * `Background Modes`. After adding this capability, check the box for `Remote notifications`. If you intend to use Time Sensitive Notifications (iOS 15+), also add the `Time Sensitive Notifications` capability. ### 3. Initialization code #### AppDelegate Add the following code to your AppDelegate class: * SwiftUI ```swift import SwiftUI import PushwooshFramework @main struct MyApp: App { // Register AppDelegate as UIApplicationDelegate @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate var body: some Scene { WindowGroup { ContentView() } } } class AppDelegate: NSObject, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Initialization code // Set custom delegate for push handling Pushwoosh.sharedInstance().delegate = self // Register for push notifications Pushwoosh.sharedInstance().registerForPushNotifications() return true } // Handle token received from APNS func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.sharedInstance().handlePushRegistration(deviceToken) } // Handle token receiving error func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.sharedInstance().handlePushRegistrationFailure(error) } //for silent push notifications func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.sharedInstance().handlePushReceived(userInfo) completionHandler(.noData) } // Fired when a push is received func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { print("onMessageReceived: ", message.payload!.description) } // Fired when a user taps the notification func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("onMessageOpened: ", message.payload!.description) } } struct ContentView: View { var body: some View { Text("Pushwoosh with SwiftUI") .padding() } } ``` * Swift ```swift import PushwooshFramework @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate, PWMessagingDelegate { var window: UIWindow? func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { //initialization code //set custom delegate for push handling, in our case AppDelegate Pushwoosh.sharedInstance().delegate = self; //register for push notifications Pushwoosh.sharedInstance().registerForPushNotifications() return true } //handle token received from APNS func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.sharedInstance().handlePushRegistration(deviceToken) } //handle token receiving error func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.sharedInstance().handlePushRegistrationFailure(error); } //for silent push notifications func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable : Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { Pushwoosh.sharedInstance().handlePushReceived(userInfo) completionHandler(.noData) } //this event is fired when the push gets received func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { print("onMessageReceived: ", message.payload!.description) } // Fired when a user taps the notification func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { print("onMessageOpened: ", message.payload!.description) } } ``` * Objective-C ```objective-c #import @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { //-----------PUSHWOOSH PART----------- // set custom delegate for push handling, in our case AppDelegate [Pushwoosh sharedInstance].delegate = self; //register for push notifications! [[Pushwoosh sharedInstance] registerForPushNotifications]; return YES; } //handle token received from APNS - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { [[Pushwoosh sharedInstance] handlePushRegistration:deviceToken]; } //handle token receiving error - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { [[Pushwoosh sharedInstance] handlePushRegistrationFailure:error]; } //for silent push notifications - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { [[Pushwoosh sharedInstance] handlePushReceived:userInfo]; completionHandler(UIBackgroundFetchResultNoData); } //this event is fired when the push gets received - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageReceived:(PWMessage *)message { NSLog(@"onMessageReceived: %@", message.payload); } //this event is fired when user taps the notification - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageOpened:(PWMessage *)message { NSLog(@"onMessageOpened: %@", message.payload); } @end ``` #### Info.plist In your `Info.plist`: * set the `Pushwoosh_APPID` key to the Pushwoosh Application Code. * set the `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) ### 4. Message delivery tracking Pushwoosh supports tracking delivery events for push notifications via the Notification Service Extension #### Add Notification Service Extension 1. In Xcode, Select **File** > **New** > **Target…** 2. Choose **Notification Service Extension** and press **Next.** 3. Enter the target name and press **Finish.** 4. When prompted for activation, press **Cancel.** #### Dependencies for the Notification Service Extension (CocoaPods only) Note: If you’re using Swift Package Manager to manage dependencies, you can skip this step, as the dependencies are added automatically. Open your `Podfile` and add the dependency for the target: Podfile ```ruby # Uncomment the next line to define a global platform for your project # platform :ios, '9.0' target 'MyApp' do # Comment the next line if you don't want to use dynamic frameworks use_frameworks! pod 'PushwooshXCFramework' end target 'MyAppNotificationExtension' do use_frameworks! pod 'PushwooshXCFramework' end ``` Run the following command in the terminal to update the dependencies: ```shell pod update ``` #### Add Pushwoosh SDK to Notification Service Extension This code allows you to intercept and process notifications within your notification extension. * Swift ```swift import UserNotifications import PushwooshFramework class NotificationService: UNNotificationServiceExtension { var contentHandler: ((UNNotificationContent) -> Void)? var bestAttemptContent: UNMutableNotificationContent? override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { // Pushwoosh ********** PWNotificationExtensionManager.shared().handle(request, contentHandler: contentHandler) // ******************** self.contentHandler = contentHandler bestAttemptContent = (request.content.mutableCopy() as? UNMutableNotificationContent) if let bestAttemptContent = bestAttemptContent { // Modify the notification content here... contentHandler(bestAttemptContent) } } override func serviceExtensionTimeWillExpire() { // Called just before the extension will be terminated by the system. // Use this as an opportunity to deliver your "best attempt" at modified content, otherwise the original push payload will be used. if let contentHandler = contentHandler, let bestAttemptContent = bestAttemptContent { contentHandler(bestAttemptContent) } } } ``` * Objective-C ```objective-c #import "PWNotificationExtensionManager.h" @interface NotificationService : UNNotificationServiceExtension @end @implementation NotificationService - (void)didReceiveNotificationRequest:(UNNotificationRequest *)request withContentHandler:(void (^)(UNNotificationContent * _Nonnull))contentHandler { // Pushwoosh ********** [[PWNotificationExtensionManager sharedManager] handleNotificationRequest:request contentHandler:contentHandler]; //********************* } @end ``` #### Info.plist In your Notification Service Extension’s Info.plist, add: * `Pushwoosh_APPID` - Your Application Code. ### 5. Run the project 1. Build and run the project. 2. Go to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push). 3. You should see the notification in the app. ## Extended Pushwoosh iOS integration At this stage, you have already integrated the SDK and can send and receive push notifications. Now, let’s explore the core functionality ### Push notifications In the Pushwoosh SDK, there are two callbacks designed for handling push notifications: * `onMessageReceived`: This method is invoked when a push notification is received. * `onMessageOpened`: This method is called when the user interacts with (opens) the notification These callbacks enable developers to manage the reception and user interaction with push notifications within their applications * Swift ```swift import PushwooshFramework class AppDelegate: NSObject, UIApplicationDelegate, PWMessagingDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool { Pushwoosh.sharedInstance().delegate = self; } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageOpened message: PWMessage) { if let payload = message.payload { print("onMessageOpened: \(payload)") } } func pushwoosh(_ pushwoosh: Pushwoosh, onMessageReceived message: PWMessage) { if let payload = message.payload { print("onMessageReceived: \(payload)") } } } ``` * Objective-C ```objective-c #import @interface AppDelegate () @end @implementation AppDelegate - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [[Pushwoosh sharedInstance] setDelegate:self]; return YES; } - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageOpened:(PWMessage *)message { if (message.payload) { NSLog(@"onMessageOpened: %@", message.payload); } } - (void)pushwoosh:(Pushwoosh *)pushwoosh onMessageReceived:(PWMessage *)message { if (message.payload) { NSLog(@"onMessageReceived: %@", message.payload); } } @end ``` ### User configuration By focusing on individual user behavior and preferences, you can deliver personalized content, leading to increased user satisfaction and loyalty * Swift ```swift import PushwooshFramework class Registration { func afterUserLogin(user: User) { let pushwoosh = Pushwoosh.sharedInstance() // set user ID if let userId = user.userId { pushwoosh.setUserId(userId) } // set user email if let userEmail = user.email { pushwoosh.setEmail(userEmail) } // set user SMS number if let userSmsNumber = user.SmsNumber { pushwoosh.registerSmsNumber(userSmsNumber) } // set user WhatsApp number if let userWhatsAppNumber = user.WhatsAppNumber { pushwoosh.registerSmsNumber(userWhatsAppNumber) } // setting additional user information as tags for Pushwoosh if let age = user.userDetails.age, let name = user.userDetails.userName, let lastLogin = user.userDetails.lastLoginDate { pushwoosh.setTags([ "age": age, "name": name, "last_login": lastLogin ]) } } } ``` * Objective-C ```objective-c #import @implementation Registration - (void)afterUserLogin:(User *)user { Pushwoosh *pushwoosh = [Pushwoosh sharedInstance]; // set user ID if (user.userId) { [pushwoosh setUserId:user.userId]; } // set user email if (user.email) { [pushwoosh setEmail:user.email]; } // setting additional user information as tags for Pushwoosh if (user.userDetails.age && user.userDetails.userName && user.userDetails.lastLoginDate) { NSDictionary *tags = @{ @"age": user.userDetails.age, @"name": user.userDetails.userName, @"last_login": user.userDetails.lastLoginDate }; [pushwoosh setTags:tags]; } } @end ``` ### Tags Tags are key-value pairs assigned to users or devices, allowing segmentation based on attributes like preferences or behavior, enabling targeted messaging. * Swift ```swift import PushwooshFramework class UpdateUser { func afterUserUpdateProfile(user: User) { let pushwoosh = Pushwoosh.sharedInstance() // set list of favorite categories pushwoosh.setTags(["favorite_categories" : user.getFavoriteCategories()]) // set payment information pushwoosh.setTags([ "is_subscribed": user.isSubscribed(), "payment_status": user.getPaymentStatus(), "billing_address": user.getBillingAddress() ]) } } ``` * Objective-C ```objective-c #import @implementation UpdateUser - (void)afterUserUpdateProfile:(User *)user { Pushwoosh *pushwoosh = [Pushwoosh sharedInstance]; // set list of favorite categories [pushwoosh setTags:@{@"favorite_categories" : user.getFavoriteCategories}]; // set payment information NSDictionary *tags = @{ @"is_subscribed": @(user.isSubscribed), @"payment_status": user.getPaymentStatus, @"billing_address": user.getBillingAddress }; [pushwoosh setTags:tags]; } @end ``` ### Events Events are specific user actions or occurrences within the app that can be tracked to analyze behavior and trigger corresponding messages or actions * Swift ```swift import PushwooshFramework class Registration { func afterUserLogin(user: User) { if let userName = user.getUserName(), let lastLogin = user.getLastLoginDate() { PWInAppManager.shared().postEvent("login", withAttributes: [ "name": userName, "last_login": lastLogin ]) } } func afterUserPurchase(user: User, product: Product) { let pushwoosh = Pushwoosh.sharedInstance() // Track purchase event PWInAppManager.shared().postEvent("purchase", withAttributes: [ "product_id": product.getId(), "product_name": product.getName(), "price": product.getPrice(), "quantity": product.getQuantity() ]) // Set user tags let lastPurchaseDate = Date().timeIntervalSince1970 let lifetimeSpend = getCurrentLifetimeSpend() + product.getPrice() pushwoosh.setTags([ "last_purchase_date": lastPurchaseDate, "lifetime_spend": lifetimeSpend ]) } } ``` * Objective-C ```objective-c #import #import @implementation Registration - (void)afterUserLogin:(User *)user { NSString *userName = [user getUserName]; NSDate *lastLogin = [user getLastLoginDate]; if (userName && lastLogin) { [[PWInAppManager sharedManager] postEvent:@"login" withAttributes:@{ @"name": userName, @"last_login": lastLogin }]; } } - (void)afterUserPurchase:(User *)user product:(Product *)product { Pushwoosh *pushwoosh = [Pushwoosh sharedInstance]; // Track purchase event [[PWInAppManager sharedManager] postEvent:@"purchase" withAttributes:@{ @"product_id": [product getId], @"product_name": [product getName], @"price": @([product getPrice]), @"quantity": @([product getQuantity]) }]; // Set user tags NSTimeInterval lastPurchaseDate = [[NSDate date] timeIntervalSince1970]; double lifetimeSpend = /* fetch current lifetime spend */ + [product getPrice]; NSDictionary *tags = @{ @"last_purchase_date": @(lastPurchaseDate), @"lifetime_spend": @(lifetimeSpend) }; [pushwoosh setTags:tags]; } @end ``` ### Rich Media Rich media refers to interactive and multimedia content, such as images, videos, or HTML, used in notifications and in-app messages to enhance user engagement * Swift ```swift import PushwooshFramework class ViewController: UIViewController, PWRichMediaPresentingDelegate { override func viewDidLoad() { super.viewDidLoad() let richMediaConfiguration = PWModalWindowConfiguration.shared() PWRichMediaManager.shared().delegate = self richMediaConfiguration.configureModalWindow(with: .PWModalWindowPositionBottom, present: .PWAnimationPresentFromBottom, dismiss: .PWAnimationDismissDown) } func richMediaManager(_ richMediaManager: PWRichMediaManager!, shouldPresent richMedia: PWRichMedia!) -> Bool { print("Rich media will be presented with: \(richMedia.pushPayload!)") return true } func richMediaManager(_ richMediaManager: PWRichMediaManager!, didPresent richMedia: PWRichMedia!) { print("Rich media has been presented with: \(richMedia.pushPayload!)") } func richMediaManager(_ richMediaManager: PWRichMediaManager!, didClose richMedia: PWRichMedia!) { print("Rich media has been closed with: \(richMedia.pushPayload!)") } func richMediaManager(_ richMediaManager: PWRichMediaManager!, presentingDidFailFor richMedia: PWRichMedia!, withError error: (any Error)!) { print("Failed to present rich media with: \(richMedia.pushPayload!). Error: \(error.localizedDescription)") } } ``` * Objective-C ```objective-c #import "ViewController.h" #import #import #import @interface ViewController () @end @implementation ViewController - (void)viewDidLoad { [super viewDidLoad]; [[PWRichMediaManager sharedManager] setDelegate:self]; [[PWModalWindowConfiguration shared] configureModalWindowWith:PWModalWindowPositionBottom presentAnimation:PWAnimationPresentFromBottom dismissAnimation:PWAnimationDismissDown]; } - (BOOL)richMediaManager:(PWRichMediaManager *)richMediaManager shouldPresentRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media will be presented with: %@", richMedia.pushPayload); return YES; } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didPresentRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media has been presented with: %@", richMedia.pushPayload); } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager didCloseRichMedia:(PWRichMedia *)richMedia { NSLog(@"Rich media has been closed with:: %@", richMedia.pushPayload); } - (void)richMediaManager:(PWRichMediaManager *)richMediaManager presentingDidFailForRichMedia:(PWRichMedia *)richMedia withError:(NSError *)error { NSLog(@"Failed to present rich media with: %@. Error: %@", richMedia.pushPayload, error.localizedDescription); } @end ``` ## Troubleshooting ### Failed to build module ‘PushwooshFramework’ When building your project, you may encounter an error similar to: ```plaintext Failed to build module 'PushwooshFramework'; this SDK is not supported by the compiler (the SDK is built with 'Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)', while this compiler is 'Apple Swift version 6.1.2 effective-5.10 (swiftlang-6.1.2.1.2 clang-1700.0.13.5)') ``` **Cause:** This error is not related to Swift compiler version incompatibility. Starting from Pushwoosh iOS SDK version 6.8.0, the SDK is modularized into several components that interact with each other. The error occurs when not all required frameworks are added to your project. **Solution:** Ensure that all four required frameworks are added to your app target when integrating via Swift Package Manager: * `PushwooshFramework` * `PushwooshCore` * `PushwooshBridge` * `PushwooshLiveActivities` To verify this in Xcode: 1. Select your project in the Project Navigator 2. Select your app target 3. Go to **General** > **Frameworks, Libraries, and Embedded Content** 4. Confirm all four frameworks are listed *** If you encounter any issues during the integration process, please refer to the [support and community](https://docs.pushwoosh.com/developer/pushwoosh-sdk/support-and-community) section. --- # iOS SDK quick start **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/quick-start/ Get started with the Pushwoosh iOS SDK using this quick and simple example --- Get started with the Pushwoosh iOS SDK using this quick and simple example. ## Prerequisites To integrate the Pushwoosh iOS SDK into your app, you will need the following: Requirements * A [Pushwoosh account](https://sso.pushwoosh.com/login) * A [Pushwoosh project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project) set up in your account. * An iOS platform configured to send push notifications. We recommend using the [Token-Based Authentication configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration/) as the simplest approach. * During setup, set the Gateway to `Sandbox` to send pushes to a simulator. * Your `Pushwoosh Application Code` and [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) from the Pushwoosh Control Panel for your application. ## Download the example project Clone the example project from GitHub: ```bash git clone https://github.com/Pushwoosh/pushwoosh-ios-sample.git ``` ## Configure the project 1. Open the project in Xcode. 2. Add `https://github.com/Pushwoosh/Pushwoosh-XCFramework` as Swift Package Dependency. 3. Set the **bundle identifier** for the main target (PushwooshSampleApp) to match your Pushwoosh project (e.g., `com.pushwoosh.PushwooshSampleApp`). 4. Use the same **bundle identifier** for the NotificationService target, appending `.NotificationService` (e.g., `com.pushwoosh.PushwooshSampleApp.NotificationService`). 5. In `Info.plist`, set the: * `Pushwoosh_APPID` key to the Pushwoosh Application Code * `Pushwoosh_API_TOKEN` key to the [Pushwoosh Device API Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) > **Important:** Be sure to give the token access to the right app in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ## Run the project 1. Build and run the project. 2. The app will have two tabs: “Actions” and “Settings”. Navigate to the **Settings** tab. 3. Tap the **Register for Pushes** button. 4. Grant permission for push notifications. The device will be registered with Pushwoosh. You should see a log entry like this: ```bash Pushwoosh: Initializing application runtime [PW] BUNDLE ID: __YOUR_BUNDLE_ID__ [PW] APP CODE: __YOUR_APP_CODE__ [PW] PUSHWOOSH SDK VERSION: 6.7.13 [PW] HWID: __YOUR_HWID__ [PW] PUSH TOKEN: (null) [PW] [I] -[PWNotificationManagerCompatiOS10] NotificationCenter authorization granted: 1 [PW] [I] -[PWPushNotificationsManagerCommon] Registered for push notifications: __YOUR_PUSH_TOKEN__ ``` Your device is now registered with Pushwoosh. ## Send a push notification Go back to the Pushwoosh Control Panel and [send a push notification](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) to your registered device. ## What’s next To learn more, please refer to the [basic integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk/basic-integration-guide/) --- # tvOS modal rich media **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/tvos-modal-rich-media/ Send Modal Rich Media to tvOS users on Apple TV --- Starting from Pushwoosh SDK version 6.11.0, you have the ability to send **Modal Rich Media** to tvOS devices (Apple TV). Modal Rich Media for tvOS provides an interactive HTML-based content display optimized for Apple TV’s remote control navigation. The rich media can be customized with different positions, animations, and focus navigation support. [](https://docs.pushwoosh.com/tv_os_rich_media.webm "tvOS Rich Media example") Caution Standard push notifications are not supported on tvOS. Apple TV does not display traditional push notification banners or alerts. Only Rich Media content delivered through silent push notifications can be displayed using the PushwooshTVOS module. > For more information on Rich Media pages, please refer to [our guide](https://docs.pushwoosh.com/product/content/rich-media). ## Installation The PushwooshTVOS module is optional and can be integrated into your tvOS project using Swift Package Manager or CocoaPods. ### Swift Package Manager Add the Pushwoosh package to your tvOS project: 1. In Xcode, select **File** → **Add Package Dependencies…** 2. Enter the repository URL: `https://github.com/Pushwoosh/Pushwoosh-XCFramework` 3. Select the latest version 4. Add the following frameworks to your tvOS target: * **PushwooshFramework** * **PushwooshCore** * **PushwooshBridge** * **PushwooshLiveActivities** * **PushwooshTVOS** ### CocoaPods Add to your Podfile: ```ruby target 'YourTvOSApp' do platform :tvos, '13.0' pod 'PushwooshXCFramework' pod 'PushwooshXCFramework/PushwooshTVOS' end ``` Then run: ```bash pod install ``` ## Basic integration ### 1. Configure Pushwoosh in your AppDelegate Import the required modules and configure Pushwoosh with your application code: ```swift import UIKit import PushwooshTVOS import PushwooshFramework @main class AppDelegate: UIResponder, UIApplicationDelegate { func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { // Configure Pushwoosh with your app code Pushwoosh.TVoS.setAppCode("XXXXX-XXXXX") // Register for push notifications Pushwoosh.TVoS.registerForTvPushNotifications() return true } } ``` ### 2. Handle device token registration Implement the device token handler to register the device with Pushwoosh: ```swift func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) { Pushwoosh.TVoS.registerForTvPushNotifications(withToken: deviceToken) { error in if let error = error { print("Failed to register: \(error)") } else { print("Successfully registered for push notifications") } } } func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) { Pushwoosh.TVoS.handleTvPushRegistrationFailure(error) } ``` ### 3. Handle incoming push notifications Process incoming push notifications with rich media content: ```swift func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any], fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) { // Handle push notification and display rich media if present if Pushwoosh.TVoS.handleTVOSPush(userInfo: userInfo) { completionHandler(.newData) } else { completionHandler(.noData) } } ``` Note tvOS only supports silent push notifications with `content-available: 1` flag. When sending push notifications to Apple TV devices through Pushwoosh, make sure to: * Enable “content-available” flag in your push notification payload * Include Rich Media content in the notification ## Rich Media configuration ### Positioning Modal Rich Media for tvOS can be positioned in five locations on the screen: ```swift // Center position (default) Pushwoosh.TVoS.configureRichMediaWith(position: .center, presentAnimation: .none, dismissAnimation: .none) // Left side of the screen Pushwoosh.TVoS.configureRichMediaWith(position: .left, presentAnimation: .fromLeft, dismissAnimation: .toLeft) // Right side of the screen Pushwoosh.TVoS.configureRichMediaWith(position: .right, presentAnimation: .fromRight, dismissAnimation: .toRight) // Top of the screen Pushwoosh.TVoS.configureRichMediaWith(position: .top, presentAnimation: .fromTop, dismissAnimation: .toTop) // Bottom of the screen Pushwoosh.TVoS.configureRichMediaWith(position: .bottom, presentAnimation: .fromBottom, dismissAnimation: .toBottom) ``` Available position options: ```swift enum PWTVOSRichMediaPosition { case center // Content positioned at the center of the screen (default) case left // Content positioned at the left side of the screen case right // Content positioned at the right side of the screen case top // Content positioned at the top of the screen case bottom // Content positioned at the bottom of the screen } ``` ### Present animations Control how rich media content appears on screen: ```swift enum PWTVOSRichMediaPresentAnimation { case none // No animation, content appears immediately (default) case fromTop // Content slides in from the top of the screen case fromBottom // Content slides in from the bottom of the screen case fromLeft // Content slides in from the left side of the screen case fromRight // Content slides in from the right side of the screen } ``` ### Dismiss animations Control how rich media content disappears from screen: ```swift enum PWTVOSRichMediaDismissAnimation { case none // No animation, content disappears immediately (default) case toTop // Content slides out to the top of the screen case toBottom // Content slides out to the bottom of the screen case toLeft // Content slides out to the left side of the screen case toRight // Content slides out to the right side of the screen } ``` ### Configuration examples Configure rich media to appear on the left side with animations: ```swift Pushwoosh.TVoS.configureRichMediaWith( position: .left, presentAnimation: .fromBottom, dismissAnimation: .toLeft ) ``` Configure rich media to appear at the bottom with slide animations: ```swift Pushwoosh.TVoS.configureRichMediaWith( position: .bottom, presentAnimation: .fromBottom, dismissAnimation: .toBottom ) ``` Configure rich media to appear centered without animations: ```swift Pushwoosh.TVoS.configureRichMediaWith( position: .center, presentAnimation: .none, dismissAnimation: .none ) ``` ## Close button configuration By default, a Close button is displayed at the bottom of rich media presentations. You can hide this button if needed: ```swift // Hide the system Close button Pushwoosh.TVoS.configureCloseButton(false) ``` Caution If you hide the Close button, ensure your rich media HTML content includes a button with the `closeInApp` action to allow users to dismiss the content: ```html ``` ## Focus navigation for Apple TV The tvOS SDK automatically manages focus navigation for Apple TV’s remote control. Interactive elements (buttons, links) in your rich media HTML content will be focusable using the Apple TV remote. ### Focus behavior * Focusable elements are automatically detected in the HTML content * Users can navigate between elements using the directional pad on the Apple TV remote * The currently focused element receives a visual highlight * Users can activate focused elements by pressing the center button on the remote * The system Close button (when visible) is also part of the focus navigation ### Best practices for HTML content When creating HTML content for tvOS rich media: 1. Use standard interactive HTML elements: `
{Fallback if a user blocks notifications}
... ``` Where: * `{domain}` — your website URL, * `{applicationCode}` — your Pushwoosh app code, * `{senderId}` — Sender ID from your Firebase console, * `{applicationServerKey}` — Server Key from your Firebase console, * `{Text on Subscribe button}` — a text on a button enabling push notifications, * `{Text on Unsubscribe button}` — a text on a button disabling push notifications, * `{Fallback if a user blocks notifications}` — a fallback block that is displayed when a user blocks push notifications. To **customize buttons’ style**, add the following code to the **** tag: css ```txt ``` To check whether you’ve done everything right, please use \ --- # Chrome and Firefox web push for HTTP websites **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/chrome-firefox-web-push-for-http-websites/ How to enable push notifications for Chrome and Firefox HTTP websites --- ATTENTION The following instructions are **unified** for Chrome and Firefox configuration **EXCEPT** for the first article, which is unique for each platform. ## Configuration 1. Select the Web push option while creating a project. 2. Choose http, enter your site URL and project name. ![New website dialog in the classic Control Panel with the http:// option in the URL address dropdown](https://docs.pushwoosh.com/web-push-notifications-chrome-firefox-web-push-for-http-websites-2.webp) 3. Configure both Chrome and Firefox with your FCM project credentials. Note Learn how to get FCM credentials in the [Chrome and Firefox configuration guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/chrome-configuration/). ## Integration **1.** Create **pushwoosh-web-pushes-http-sdk.js** file in the root directory of your website with the following content: ```javascript var pushwoosh = { PUSHWOOSH_APPLICATION_CODE: 'XXXX-XXXX', PUSHWOOSH_APPLICATION_CODE_GET_PARAMETER: 'pw_application_code', init: function (applicationCode) { this.PUSHWOOSH_APPLICATION_CODE = applicationCode; window.addEventListener('message', this.pwReceiveMessage, false); }, tryInitUsingGetParameter: function () { var applicationCode = this.getQueryVariable(this.PUSHWOOSH_APPLICATION_CODE_GET_PARAMETER); console.log(applicationCode); if (applicationCode) { this.init(applicationCode); } }, pwReceiveMessage: function (event) { if (event.data == 'allowPushNotifications') { localStorage.setItem('pwAllowPushNotifications', true); } }, isBrowserChrome: function () { return navigator.userAgent.toLowerCase().indexOf('chrome') > -1; }, isBrowserFirefox: function () { return navigator.userAgent.toLowerCase().indexOf('firefox') > -1; }, isBrowserSafari: function () { return navigator.userAgent.toLowerCase().indexOf('safari') > -1 && !this.isBrowserChrome(); }, isBrowserSupported: function () { return this.isBrowserChrome() || this.isBrowserFirefox(); }, subscribeAtStart: function () { if (this.isBrowserSupported()) { if (null === localStorage.getItem('pwAllowPushNotifications')) { this.showSubscriptionWindow(); } } }, isSubscribedForPushNotifications: function () { return true == localStorage.getItem('pwAllowPushNotifications'); }, showSubscriptionWindow: function () { if (this.isBrowserSupported()) { var windowWidth = screen.width / 2; var windowHeight = screen.height / 2; var windowLeft = screen.width / 2 - windowWidth / 2; var windowRight = screen.height / 2 - windowHeight / 2; var URL = 'https://' + this.PUSHWOOSH_APPLICATION_CODE + '.chrome.pushwoosh.com/'; var pwSubscribeWindow = window.open(URL, '_blank', 'width=' + windowWidth + ',height=' + windowHeight + ',resizable=yes,scrollbars=yes,status=yes,left=' + windowLeft + ',top=' + windowRight); } }, getQueryVariable: function (variable) { // document.currentScript won't work if this code is called from function in event lister if (document.currentScript) { var urlParts = document.currentScript.src.split('?'); if (typeof urlParts[1] !== 'undefined') { var vars = urlParts[1].split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (pair[0] == variable) { return pair[1]; } } } } else { console.error('Cannot get current script address'); } return null; } }; pushwoosh.tryInitUsingGetParameter(); ``` **2.** Include the previous file to your website and initialize it using the **application code** instead of XXXXX-XXXXX ```javascript ``` **3.** In order to create a Push Subscription button use the following: ```txt ``` **4.** Alternatively, if you want Notification Subscriptions to pop up automatically (opposed to #4 above), use the following: ```javascript ``` Note #### IMPORTANT A pop-up window is only allowed with the user’s permission due to the browser’s security policy: pop-up blockers engage automatically without direct user action.\ The browser will block pop-ups automatically until you explicitly allow them with a button click.\ The above-mentioned Push Subscription button will work flawlessly without additional user permissions. As a result, the user will be prompted to subscribe for push notifications from the website: --- # Custom subscription Popup **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/ This guide addresses implementing a customizable subscription prompt on your website --- Superseded by the Subscription widget The [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/)’s prompt replaces this popup once turned on (card, bar, or modal layout), independently of its bell. This page still applies to a `subscribePopup` already integrated on your site. Use the Subscription widget for new integrations. **Custom Subscription Popup** is a pop-up window displayed on the web page and suggesting users to subscribe to web push notifications. It’s implemented with [Pushwoosh WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration) and allows to overcome the browser restrictions on a native subscription prompts displaying. A native subscription popup can be shown to a particular user only twice. If a user doesn’t allow push notifications and closes the native subscription popup two times in a row, it won’t be shown again and push notifications are considered blocked. A **Custom Subscription Popup** in its turn can be displayed as many times as needed unless a user subscribes to or blocks push notifications explicitly. A **Custom Subscription Popup** can contain any text explaining the advantages of subscription to push notifications and two customizable buttons — a one for an “Ask later” option and a one for “Subscribe.” Once a user presses a “Subscribe” button, a native subscription popup appears. When a user presses an “Ask Later” button or simply closes the window, the Custom Subscription Popup will appear again in a specified time (by default, in a week after; a delay to retry can be customized via the “retryOffset” parameter). ## Implementation To enable and customize a **Custom Subscription Popup**, use the following script while initializing [Pushwoosh WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration): Example ```javascript Pushwoosh.push('init', { // . . ., subscribePopup: { enable: true, // (boolean) popup activation text: 'Text on popup', // (string) a text to display on the popup confirmSubscriptionButtonText: 'Subscribe', // (string) custom text for the “Subscribe” button manualToggle: false, // (boolean) manual control for showing the popup delay: 60, // (integer) a delay between the page loading and popup appearance retryOffset: 604800, // (integer) an offset in seconds to display the popup again iconUrl: 'https://...', // (string) custom icon URL iconAlt: 'Descriptive text for your icon', // (string) ALT text for the icon overlay: false, // (boolean) enables page overlaying when popup is displayed position: 'top', // (string) position on the page. Possible values: ’top' | 'center' | 'bottom’ mobileViewMargin: '10px', // (string) margin for mobile view bgColor: '#fff', // (string) popup’s background color borderColor: 'transparent', // (string) popup’s border color boxShadow: '0 3px 6px rgba(0,0,0,0.16)', // (string) popup’s shadow textColor: '#000', // (string) popup’s text color textSize: 'inherit', // (string) popup’s text size textWeight: 'normal', // (string) popup’s text weight fontFamily: 'inherit', // (string) popup’s text font subscribeBtnBgColor: '#4285f4', // (string) “Subscribe” button’s background color subscribeBtnTextColor: '#fff', // (string) “Subscribe” button text’s color subscribeBtnTextWeight: 'bold', // (string) “Subscribe” button text weight subscribeBtnBorderColor: 'transparent', // (string) “Subscribe” button border color subscribeBtnBorderRadius: '4px', // (string) “Subscribe” button border radius askLaterButtonText: 'Not now', // (string) custom text for the “Ask later” button askLaterBtnBgColor: 'transparent', // (string) “Ask later” button’s background color askLaterBtnTextColor: '#000', // (string) “Ask later” button text’s color askLaterBtnBorderColor: 'transparent', // (string) “Ask later” button border color askLaterBtnBorderRadius: '4px', // (string) “Ask later” button border radius askLaterBtnTextWeight: 'normal', // (string) “Ask later” button text weight theme: 'material', // (string) popup theme, possible values: 'material' | 'topbar' viewport: 'desktop' // (string) target viewport, possible values: 'desktop' | 'mobile' } }); ``` ### Accessing the Custom Subscription Popup Instance The **Custom Subscription Popup** instance is available via `Pushwoosh.moduleRegistry.subscribePopup`. **Note:** This instance becomes accessible only after the `subscribe-popup-ready` event is triggered. However, it will **not be available if the user is already subscribed to push notifications.** The instance provides a `toggle()` method that allows you to manually show or hide the popup: ```javascript // Show or hide the Custom Subscription Popup manually Pushwoosh.moduleRegistry.subscribePopup.toggle(); // Toggles visibility Pushwoosh.moduleRegistry.subscribePopup.toggle(true); // Shows the popup Pushwoosh.moduleRegistry.subscribePopup.toggle(false); // Hides the popup ``` This allows for more control over the popup’s behavior directly from your code. ## Themes By default, the Subscription Popup uses a **‘material’ theme** and overlays some part of a screen when shown, thus hiding some content from a website visitor. !['Material' theme](https://docs.pushwoosh.com/web-push-notifications-custom-subscription-popup-1.webp) To avoid hiding valuable content by the Subscription Popup, use the **‘topbar’ theme** so that the popup is shown as a narrow bar on the top of the screen. !['Topbar' theme](https://docs.pushwoosh.com/web-push-notifications-custom-subscription-popup-2.webp) ## Custom Subscription Popup Events The following events are available for tracking interactions with the **Custom Subscription Popup**: ```javascript // Executed when the custom subscription popup is loaded and ready to be shown // Note: This event will NOT trigger if the user is already subscribed to push notifications. Pushwoosh.addEventHandler('subscribe-popup-ready', function() { console.log("Triggered event: subscribe-popup-ready"); }); // Executed when the custom subscription popup is shown Pushwoosh.addEventHandler('subscribe-popup-show', function() { console.log("Triggered event: subscribe-popup-show"); }); // Executed when a user clicks on the 'Subscribe' button in the pop-up window Pushwoosh.addEventHandler('subscribe-popup-accept', function() { console.log("Triggered event: subscribe-popup-accept"); }); // Executed when a user clicks on the 'Ask later' button in the pop-up window Pushwoosh.addEventHandler('subscribe-popup-decline', function() { console.log("Triggered event: subscribe-popup-decline"); }); // Executed when the popup hides Pushwoosh.addEventHandler('subscribe-popup-hide', function() { console.log("Triggered event: subscribe-popup-hide"); }); ``` --- # Feature flags in the Web SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/ Read Pushwoosh feature flags and their properties on your website, react to updates, and log impressions for comparison. --- `Pushwoosh.featureFlags` gives your website the [feature flags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) configured for the application in the Control Panel. Pushwoosh evaluates the flag rules on the server for the current device and user, and the SDK returns the result: whether the flag is on for this visitor and which property values apply. Beta feature Feature flags are in beta and available only on plans that include them. On other plans the server returns no flags, so every flag falls back to the default you pass in code. ## Prerequisites * Web SDK 3.94.1 or later, loaded from the Pushwoosh CDN as described in the [Web SDK 3.0 guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration), or the `web-push-notifications` [npm package](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/usage-with-npm/) 3.95.0 or later. * At least one flag created in **Audience** → **Feature Flags**. You need its **Key** and the keys and types of its properties. Use the keys exactly as they appear there, letter case included. For example, `get('New_Checkout')` doesn’t find a flag with the key `new_checkout`. ## Read a flag Read flags after the SDK is ready. Always pass a default: it applies until the SDK has flags for the page, and whenever the flag doesn’t exist, is archived, or the plan doesn’t include feature flags. ```javascript Pushwoosh.push(() => { const flags = Pushwoosh.featureFlags; const render = () => { const checkout = flags.get('new_checkout'); if (checkout && checkout.enabled) { showNewCheckout({ buttonColor: checkout.getString('button_color', 'blue'), maxItems: checkout.getNumber('max_items', 3), }); } else { showOldCheckout(); } }; render(); flags.addUpdateListener(render); }); ``` With the npm package, use the `Pushwoosh` instance you created: ```javascript import { Pushwoosh } from 'web-push-notifications'; const pushwoosh = new Pushwoosh(); pushwoosh.push(['init', { applicationCode: 'XXXXX-XXXXX' /* ... */ }]); pushwoosh.push(() => { const isNewCheckout = pushwoosh.featureFlags.isEnabled('new_checkout', false); // ... }); ``` The SDK makes no flag requests on a page until your code first calls `get()`, `isEnabled()`, `refresh()`, or `addUpdateListener()`, so pages that don’t use flags send no extra traffic. ## What a flag returns `get(key)` returns a `FeatureFlag` object, or `null` when the SDK has no flags for the page yet or the flag isn’t in the answer. An archived flag is not in the answer. The object has these fields: | Field | Type | Description | | :-------- | :------ | :--------------------------------------------------------------------------------------------------------------------------------------------------- | | `key` | string | The flag key. | | `enabled` | boolean | Whether the flag is on for this visitor. | | `ruleId` | string | The rule that decided the result, for example `r_1a2b3c4d`. `default` when no rule matched the visitor, and an empty string when the flag is killed. | Read property values with the getter that matches the property type. Each getter returns your default when the property is missing or holds a value of another type. When the flag is off, the properties hold the default values set in the Control Panel. | Method | Returns | | :---------------------------------- | :-------------------------------------------------------- | | `getString(property, defaultValue)` | The value of a **String** property. | | `getNumber(property, defaultValue)` | The value of a **Number** property. | | `getBool(property, defaultValue)` | The value of a **Boolean** property. | | `getJson(property, defaultValue)` | The value of a **JSON** property, as a parsed JSON value. | | `getProperties()` | A copy of all property values of the flag. | `isEnabled(key, defaultValue)` is a shortcut for `get(key)?.enabled`, returning `defaultValue` when there is no flag. ## Keep flags up to date The SDK stores the last answer in the browser’s IndexedDB for up to 24 hours. The stored flags are valid only for the User ID they were requested for. The SDK refreshes them: * on the first use on a page, when the stored flags are older than 15 minutes or missing; * every hour while the page stays open; * after the device registers, when the flags were requested before registration; * right after the User ID changes. The stored flags of the previous user are discarded, so a new user never sees them. To react to new values, register a listener. It’s called after a refresh that changed any flag value: ```javascript const onFlagsChanged = () => render(); Pushwoosh.featureFlags.addUpdateListener(onFlagsChanged); // later Pushwoosh.featureFlags.removeUpdateListener(onFlagsChanged); ``` To request the flags yourself, for example after an action that may move the visitor into a different segment, call `refresh()`. While the stored flags are valid, it sends at most one request per 5 minutes and resolves immediately within that window. The promise rejects when the request fails, and the stored flags stay in use. ```javascript try { await Pushwoosh.featureFlags.refresh(); } catch (error) { // the stored flags, or your defaults, stay in effect } render(); ``` If Pushwoosh can’t match the visitor’s segments in time, it returns a partial answer. The SDK uses it only on the current page, only when it has no valid stored flags, and never stores it. The next refresh brings the full answer. When a flag is killed in the Control Panel, visitors get it off with default property values at the next refresh. When the SDK can’t reach the server, it keeps using the stored flags until they are 24 hours old, and your defaults after that. ## Log impressions An impression tells Pushwoosh that a visitor actually reached the code a flag controls, so you can compare visitors who got the feature with those who didn’t. Call `logImpression(key)` at the moment the visitor sees the feature or its old version, for both the enabled and the disabled branch, because visitors with the flag off are your comparison group: ```javascript const checkout = Pushwoosh.featureFlags.get('new_checkout'); if (checkout) { Pushwoosh.featureFlags.logImpression('new_checkout'); } ``` The SDK posts the `PW_FeatureFlagImpression` event with these attributes: | Attribute | Type | Value | | :--------- | :------ | :------------------------------------------------ | | `flag_key` | string | The flag key. | | `rule_id` | string | The rule that decided the result, as in `ruleId`. | | `enabled` | boolean | Whether the flag was on. | You don’t need to create the event in advance, because Pushwoosh adds `PW_FeatureFlagImpression` with these attributes to **Audience** → **Events** when the first impression arrives. The SDK sends one impression per flag, rule, and state on a page, and skips repeats from other tabs of the same browser for 30 minutes. `logImpression()` never rejects, and it sends nothing when there is no flag for the key. Impressions also let marketers build a segment of visitors who reached the feature: the **saw the feature (30 days)** state of the [feature flag condition](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature). ## Method reference `Pushwoosh.featureFlags` has these methods: | Method | Description | | :------------------------------- | :------------------------------------------------------------------------------------------------------------- | | `get(key)` | Returns the `FeatureFlag` for the key, or `null`. Reads the stored flags only and never waits for the network. | | `isEnabled(key, defaultValue)` | Returns whether the flag is on, or `defaultValue` when there is no flag. | | `refresh()` | Requests the flags, throttled to once per 5 minutes while the stored flags are valid. Returns a promise. | | `addUpdateListener(listener)` | Calls `listener` after every refresh that changed a flag value. | | `removeUpdateListener(listener)` | Removes a listener added with `addUpdateListener`. | | `logImpression(key)` | Posts a `PW_FeatureFlagImpression` event for the flag’s current rule and state. Returns a promise. | --- # Web push on iPhone and iPad **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/ios-web-push/ Set up web push for iPhone and iPad on iOS and iPadOS 16.4 or later, where users subscribe after adding a site to the Home Screen --- iPhone and iPad users on iOS and iPadOS 16.4 or later can receive web push notifications from your website after they add it to their Home Screen. ## When web push works on iPhone and iPad Web push on iOS and iPadOS works only for a website opened from its Home Screen icon. In a regular Safari tab, the browser doesn’t offer push at all, so the Web SDK initializes without push and the visitor can’t subscribe. To receive notifications, the user needs: * An iPhone or iPad on iOS or iPadOS 16.4 or later. * Your website added to the Home Screen and opened from that icon. * Notifications allowed in response to a tap on your site. Tip We recommend letting iPhone and iPad visitors know on your site that they need to add it to the Home Screen to get notifications. To do it, they tap **Share** in Safari, then **Add to Home Screen** and **Add**, and open the site from the new icon on their Home Screen. ## What your website needs Integrate the Web SDK the same way as for desktop browsers, then add a web app manifest: 1. Integrate the SDK and the service worker by following [Web push SDK 3.0](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/). If your site already has its own service worker, follow [Progressive Web App support](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#progressive-web-app-support). 2. Add a web app manifest with `display` set to `standalone` or `fullscreen`, and link it from every page: manifest.json ```json { "name": "Example Store", "short_name": "Store", "start_url": "/", "display": "standalone", "icons": [ { "src": "/icon-192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/icon-512.png", "sizes": "512x512", "type": "image/png" } ] } ``` ```html ``` 3. Ask for notification permission from a user tap. iOS and iPadOS show the permission dialog only in response to a direct user action, so use the [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/) or a button that calls `Pushwoosh.subscribe()`: ```html ``` ## How to find iPhone and iPad subscribers in Pushwoosh Pushwoosh registers iPhone and iPad web subscribers under the Chrome platform, so statistics don’t show them separately and no default tag reliably identifies the device. To target these subscribers, set a tag of your own from the website: 1. [Create a custom tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#how-to-set-up-a-custom-tag) in the Control Panel before the site sends it, for example a String tag named `Web App Platform`. Clear the **User-specific** checkbox, which is selected by default. This makes a **General** tag, stored per device. Without this step, `setTags` creates the tag automatically as [User-specific](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#tag-scope-general-vs-user-specific). Its value is shared by all devices with the same User ID, so once the site calls `registerUser`, the user’s desktop browser also gets `ios` and receives the messages you target at iPhone and iPad subscribers with this tag. 2. Set the tag when the site runs from the Home Screen on iPhone or iPad: ```javascript Pushwoosh.push((api) => { // iPadOS Safari reports a Mac user agent, so a touch screen marks an iPad const isIos = /iPhone|iPad/.test(navigator.userAgent) || (/Macintosh/.test(navigator.userAgent) && navigator.maxTouchPoints > 1); const isIosHomeScreen = isIos && (window.navigator.standalone === true || window.matchMedia('(display-mode: standalone)').matches); if (isIosHomeScreen) { api.setTags({ 'Web App Platform': 'ios' }); } }); ``` 3. Subscribe from a test iPhone or iPad. 4. Check the tag value on that device in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#devices-and-platforms-tab). `setTags` returns success even when Pushwoosh doesn’t save the value, for example when tag auto-creation is turned off for the account, so the code alone doesn’t confirm it was saved. 5. [Build a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) on this tag to send a message only to iPhone and iPad subscribers. --- # Manage User Consent for Web Push Notifications **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/manage-user-consent/ Learn how to manage user consent for web push notifications, including subscription prompts, default widgets, and custom widgets. --- By default, the Pushwoosh SDK shows the native subscription prompt as soon as it is initialized. However, you might want to ask for user consent at a more appropriate time, for example, after the user has had a chance to interact with your website. ### Disabling Automatic Subscription To prevent the SDK from automatically showing the subscription prompt upon initialization, set the `communicationEnabled` parameter to `false` in the `init` call. This gives you control over when to ask for push notification permission. ```html ``` ### Enabling Subscription on User Consent Once you have disabled automatic subscription, you can prompt the user to subscribe at any time. When the user agrees to receive push notifications (e.g., by clicking a “Subscribe” button on your custom UI), you can enable communication by calling the `setCommunicationEnabled` method. `Pushwoosh.setCommunicationEnabled(status?: boolean): Promise` Calling `Pushwoosh.setCommunicationEnabled(true)` enables communication with Pushwoosh services. Once enabled, the SDK will proceed to show the native browser permission prompt. Here is an example of how you might use this method: ```javascript // Assuming you have a subscribe button with id="subscribe-button" const subscribeButton = document.getElementById('subscribe-button'); subscribeButton.addEventListener('click', () => { Pushwoosh.setCommunicationEnabled(true) .then(() => { console.log('User is subscribed to push notifications.'); // You can hide the subscribe button now subscribeButton.style.display = 'none'; }) .catch((error) => { console.error('Error subscribing user:', error); }); }); ``` --- # On-site product recommendations **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/ Show product recommendation blocks on your website with the Pushwoosh Web SDK, using a ready-made widget or your own markup. --- The Web SDK can show product recommendations on your website: on a product page, in the cart, on a catalog page, or on a 404 page. Products come from the application’s [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/). There are two ways to show them: * **Widget:** add an element with the `data-pw-recommendations` attribute, and the SDK fills it with a grid of products. * **Headless API:** call `Pushwoosh.recommendations.fetch` and render the products with your own markup. A live example is the Pushwoosh demo shop at [pushon.pushwoosh.com](https://pushon.pushwoosh.com/). It has a widget block on the 404 page and headless blocks on the product page and in the cart. ## Prerequisites * Web SDK 3.96.0 or later, loaded from the Pushwoosh CDN as described in the [Web SDK 3.0 guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration). The `web-push-notifications` npm package doesn’t include recommendations yet. * Products in the application’s [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/). * On-site recommendations turned on for the application. See [Turn on on-site recommendations](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#turn-on-on-site-recommendations). ## Turn on on-site recommendations Each application opts in separately, because the feed that serves the blocks is public and doesn’t take an API token. Until then, the feed answers with an error and blocks stay empty. To turn on on-site recommendations in the Control Panel: 1. Go to **Content** → **Product Catalog** for the application. 2. Click **On-site recommendations**. 3. Turn on **Show recommendations on my website**. 4. In **Website domains**, enter the domains of the websites that will show the blocks, one per line. For example, `shop.example.com`. 5. Click **Save**. ![On-site recommendations panel with the Show recommendations on my website switch turned on, one domain in Website domains, and the Save button](https://docs.pushwoosh.com/onsite-recommendations-cp-drawer.webp) After you save: * **Website domains** shows the domains as Pushwoosh stored them: with `https://` added, in lowercase, and without duplicates. Check the list for typos. * Blocks load only on the listed websites. If you leave **Website domains** empty, blocks load on any website. * The change takes effect within a minute. If you can’t access the Control Panel, ask Pushwoosh support or your Customer Success Manager to turn on on-site recommendations for the application. Include the [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) and the domains of your websites. ## Recommendation strategies | Strategy | What it shows | Needs | | :---------------- | :-------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `viewed` | Products this visitor viewed recently. For a signed-in visitor, products viewed under their user ID come first. | Product views sent with [`trackProductView`](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#track-product-views). | | `bestsellers_30d` | The application’s best-selling products over the last 30 days. Sold-out products are skipped. | [Order events](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#send-order-events) with the order’s items. | | `also_bought` | Products that are often bought or viewed together with a given product. Sold-out products are skipped. | Product views sent with [`trackProductView`](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#track-product-views), [order events](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#send-order-events), or any other events that carry a product ID. See [Track product activity](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/#based-on-what-they-viewed-and-bought-together-track-product-activity). The request needs a product ID. | When a strategy has nothing to show, the feed serves the fallback strategy instead. The fallback is `bestsellers_30d` by default. Set it to `none` to get an empty answer instead. The answer always tells you which strategy was actually served, so you can change the block’s title. ## Track product views The `viewed` and `also_bought` strategies use product views. On every product page, call `trackProductView` after the SDK is ready: ```javascript Pushwoosh.push(() => { Pushwoosh.trackProductView({ id: 'SKU-123', name: 'Linen shirt', price: 49.9, currency: 'USD', category: 'Shirts', url: 'https://shop.example.com/linen-shirt', imageUrl: 'https://shop.example.com/img/linen-shirt.jpg', }); }); ``` Only `id` is required. Use the same product ID as in your Product catalog. The SDK sends a `PW_ProductView` event and skips repeat views of the same product within 30 seconds, so a page reload doesn’t count as a second view. ## Send order events The `bestsellers_30d` strategy counts only sales from order events, and it’s the default fallback for every block. When a visitor places an order, send `PW_OrderCreated`: ```javascript Pushwoosh.push((api) => { api.postEvent('PW_OrderCreated', { orderId: 'ORDER-10293', items: [ { productId: 'SKU-123', quantity: 1 }, { productId: 'SKU-456', quantity: 2 }, ], }); }); ``` `orderId` is required, because an order without it isn’t counted. In `items`, use the same product IDs as in your Product catalog. When an order changes, send `PW_OrderUpdated` with the same `orderId` and `items`. To send order events from your server instead, see [Bestsellers: add items to your order events](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/#bestsellers-add-items-to-your-order-events). ## Add a widget block Add an element with the `data-pw-recommendations` attribute where the block should appear. The SDK loads the widget only on pages that have such an element. ```html
``` | Attribute | Description | | :------------------------ | :------------------------------------------------------------------------------------------------------------------------------------- | | `data-pw-recommendations` | Marks the element as a recommendations block. Required. | | `data-pw-strategy` | `viewed`, `bestsellers_30d`, or `also_bought`. Defaults to `bestsellers_30d`. | | `data-pw-fallback` | Strategy to serve when the main one has nothing: `viewed`, `bestsellers_30d`, `also_bought`, or `none`. Defaults to `bestsellers_30d`. | | `data-pw-limit` | Number of products, from 1 to 12. Defaults to 6. | | `data-pw-product-id` | The product on screen. Required for `also_bought`. | | `data-pw-exclude` | Comma-separated product IDs not to show, for example the products already in the cart. Up to 50. | | `data-pw-block` | Block name. It’s added to product links and to click events, so you can tell blocks apart in analytics. | | `data-pw-title` | Block title. | | `data-pw-fallback-title` | Title to show when the fallback strategy was served, for example “Popular right now” instead of “You viewed”. | | `data-pw-min-height` | Space to reserve while the block loads, to avoid a layout shift. A number is read as pixels. Any CSS length also works. | The widget shows each product’s image, name, and price, and links to the product page. Product links carry `utm_medium=onsite`. A click on a product sends a `PW_RecommendationClick` event with the block name, the strategy, and the product’s position. When you change any `data-pw-*` attribute, the block loads again. For example, update `data-pw-product-id` when the product on screen changes without a page reload. ### Blocks for typical pages | Page | Strategy | Attributes to set | | :------------------- | :---------------- | :------------------------------------------------------------------------------------------------------------- | | Product page | `also_bought` | `data-pw-product-id` with the product on screen, `data-pw-exclude` with the same ID | | Cart | `also_bought` | `data-pw-product-id` with a product in the cart, `data-pw-exclude` with all products in the cart | | Catalog or home page | `bestsellers_30d` | — | | 404 page | `viewed` | `data-pw-fallback="bestsellers_30d"` and `data-pw-fallback-title`, so first-time visitors see popular products | ### Block states The widget sets the `data-pw-state` attribute on the block element: * **`loading`:** products are loading. * **`ready`:** products are shown. * **`empty`:** neither the strategy nor the fallback has products. The block stays empty. * **`error`:** the products didn’t load. The block stays empty. Use the state to hide your own frame or heading around an empty block: ```css .recommendations-frame:has([data-pw-state="empty"], [data-pw-state="error"]) { display: none; } ``` ### Style the widget The widget renders inside a shadow DOM, so your site’s CSS doesn’t reach its content. Set these custom properties on the block element instead: | Property | Description | Default | | :------------------ | :---------------------------------------------------------------------------------- | :---------------------- | | `--pw-reco-font` | Font family | Inherited from the page | | `--pw-reco-accent` | Price color | Current text color | | `--pw-reco-radius` | Corner radius of product images | `8px` | | `--pw-reco-columns` | Number of grid columns. On screens up to 600px wide, the grid always has 2 columns. | As many as fit | ```css [data-pw-recommendations] { --pw-reco-columns: 4; --pw-reco-radius: 0; --pw-reco-accent: #337ab7; } ``` For finer styling, the title, the grid, and each product card are exposed as the `title`, `grid`, and `card` [CSS parts](https://developer.mozilla.org/en-US/docs/Web/CSS/::part), for example `[data-pw-recommendations]::part(card)`. ### Single-page applications By default, the SDK looks for blocks once, when it’s ready. If your site renders blocks later, for example on navigation in a single-page application, set `recommendations.spa` to `true` in the `init` call: ```javascript Pushwoosh.push(['init', { applicationCode: 'XXXXX-XXXXX', // other initialization parameters... recommendations: { spa: true, }, }]); ``` Replace `XXXXX-XXXXX` with your [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). The SDK then keeps watching the page and loads the widget once a block appears. To keep the widget off even when a page has `data-pw-recommendations` elements, set `recommendations.widget` to `false`. The headless API keeps working. ## Render blocks with your own markup Use the headless API when the widget’s layout doesn’t fit your design. Request products with `Pushwoosh.recommendations.fetch` and report clicks with `Pushwoosh.recommendations.trackClick`: ```javascript Pushwoosh.push(async () => { const request = { strategy: 'also_bought', productId: 'SKU-123', exclude: ['SKU-123'], fallback: 'bestsellers_30d', limit: 4, block: 'pdp-also-bought', }; const { items, strategy } = await Pushwoosh.recommendations.fetch(request); const title = strategy === request.strategy ? 'Bought together' : 'Popular right now'; renderBlock(title, items, (item, index) => { Pushwoosh.recommendations.trackClick({ block: request.block, strategy, productId: item.id, position: index + 1, }); }); }); ``` `fetch` takes the same parameters as the widget attributes: `strategy`, `fallback`, `limit`, `productId`, `exclude`, and `block`. It returns: * **`items`:** the products. Each has `id`, `title`, `description`, `image_url`, `url`, `price`, `price_formatted`, `old_price_formatted` (only for a product on sale), `currency`, `category`, and `sku`. * **`strategy`:** the strategy actually served. It differs from the requested one when the fallback was served, and it’s `none` when the answer is empty. If the request fails, `fetch` throws an error. Catch it and hide the block. `trackClick` sends the `PW_RecommendationClick` event. It’s delivered even when the click opens another page. `position` is the product’s place in the block, starting from 1. ## Behavior without consent to communication If communication with Pushwoosh is disabled for the visitor, for example with `communicationEnabled: false` in the `init` call (see [Manage user consent](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/manage-user-consent/)): * `trackProductView` and `trackClick` send nothing. * Blocks still show products, but the request doesn’t identify the visitor. The `viewed` strategy has no history to use, so the fallback strategy is served. Once the visitor enables communication, product views and clicks are tracked again. --- # Push subscription button **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/push-subscription-button/ How to implement a Push Subscription Button on your website --- Superseded by the Subscription widget The [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/)’s bell replaces this button, switched on independently of its prompt. This page still applies to a `subscribeWidget` already integrated on your site. Use the Subscription widget for new integrations. Get more push subscribers and improve user experience! Set up a custom subscription button on your website to increase opt-in rate. Instead of automatically showing newcomers the standard dialog, explain the value of your messages and prompt to subscribe for push notifications when users are happy.\ In **two** easy steps, you’ll have a custom push subscription button ready and working! ## 1. Implement Web SDK subscription button Caution #### Pushwoosh Web SDK Make sure you’ve implemented Pushwoosh Web SDK on your website. To do so, please follow our [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration). To show the button on your website, please add the following code into initialization parameters of Pushwoosh Web SDK: ```javascript Pushwoosh.push(['init', { //... autoSubscribe: false, subscribeWidget: { enable: true, } }]); ``` Make sure to disable the `autoSubscribe` when setting up the button! Note To make the button visible for visitors using Chrome and Mozilla, you need to specify the `fcm_sender_id` in `manifest.json` file; for Safari, specify `safariWebsitePushID`. ## 2. Customize the button To make the button match your website’s look and feel, customize its design! You can change such parameters as the bell’s color, position, border, and shadow; moreover, it’s possible to change the tooltip texts. ```javascript Pushwoosh.push(['init', { //... subscribeWidget: { enable: true, position: 'bottomLeft', //possible values: ‘bottomLeft’, ‘bottomRight’, ‘topLeft’, ‘topRight’ bgColor: '#12AE7E', bellColor: 'white', bellStrokeColor: '#08754f', bellButtonBorder: '1px solid #379676', shadow: '0px 0px 6px rgba(0, 0, 0, 0.75)', size: '48px', indent: '20px', zIndex: '999999', tooltipText: { successSubscribe: 'You are successfully subscribed!', needSubscribe: 'Get notifications about important news!', blockSubscribe: 'Click to see how to get notifications', alreadySubscribed: 'You are already subscribed' } buttonImage: 'https://url-image.jpg', contentImages: { chrome: 'https://url-image.jpg', mobileChrome: 'https://url-image.jpg', firefox: 'https://url-image.jpg', opera: 'https://url-image.jpg', safari: 'https://url-image.jpg', edge: 'https://url-image.jpg', } } }]); ``` Tip To maximize the push opt-in rates, make sure the benefits of subscribing are obvious to users. Change `needSubscribe` text so it would explain the value of your messages, and people will subscribe for notifications! --- # Subscription widget **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/ Configure the Subscription widget for web push notifications, an invitation prompt and a launcher bell switched on independently --- The **Subscription widget** is a prompt and a launcher bell that invite website visitors to subscribe to push notifications, configured together as `subscriptionWidget` but switched on independently: the prompt alone, the bell alone, or both at once. Turning on either one replaces the [push subscription button](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/push-subscription-button/), the [Custom Subscription Popup](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/), and the [Subscription Prompt](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/) — while at least one part is on, none of those three older widgets load on the page. A visitor who has not decided yet sees the prompt’s ask. A visitor who already blocked notifications sees an explanation of how to unblock them instead. ## Prerequisites Make sure you’ve implemented Pushwoosh Web SDK 3.0 on your website. To do so, follow our [integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration). ## 1. Turn the prompt and the bell on Add the `subscriptionWidget` parameter to the WebSDK initialization and set `prompt.enabled` and/or `launcher.enabled` to `true`: ```javascript Pushwoosh.push(['init', { //... subscriptionWidget: { prompt: { enabled: true }, launcher: { enabled: true }, } }]); ``` No single top-level switch A top-level `subscriptionWidget.enable` is not read. Setting only that field turns nothing on. See [Migrating from the older widgets](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/#migrating-from-the-older-widgets) below. Every field below is optional. An omitted one falls back to its default. In the init snippet, a number outside its range is silently clamped to the nearest bound and an invalid enum value silently falls back to its default, with no console warning. The Control Panel forms described further down validate each field and refuse to save an invalid one. ## 2. Configure the prompt and the bell ```javascript Pushwoosh.push(['init', { //... subscriptionWidget: { prompt: { enabled: true, layout: 'card', // 'card' | 'bar' | 'modal' position: 'bottom-right', iconUrl: 'https://url-image.png', borderRadius: 12, // 0–32 overlay: false, }, launcher: { enabled: true, position: 'bottom-right', // 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right' offset: 20, // 0–100 width: 48, // 16–200 height: 48, // 16–200 imageUrl: 'https://url-image.png', background: '#4285f4', iconColor: '#ffffff', }, display: { trigger: 'auto', // 'auto' | 'launcher' | 'manual' delay: 5, // seconds, 0–3600 cappingCount: 3, // auto-shows per visitor, 0–100, 0 = unlimited cappingInterval: 24, // hours between auto-shows, 0–8760 }, texts: { title: 'Stay in the loop', message: 'Get notified about news and updates.', acceptText: 'Allow', declineText: 'Not now', closeText: 'Close', blockedTitle: 'Notifications are blocked', blockedMessage: 'Enable notifications in your browser settings to stay updated.', launcherLabel: 'Subscribe to notifications', launcherBlockedLabel: 'Notifications are blocked', }, colors: { background: '#ffffff', accent: '#4285f4', text: '#000000', textSecondary: '#666666', }, } }]); ``` ### Layout and position `prompt.layout` sets the shape of the prompt: * **`card`** — a compact card anchored to an edge or a corner. `prompt.position` accepts `top`, `bottom`, `top-left`, `top-right`, `bottom-left`, or `bottom-right`. * **`bar`** — a bar spanning the full width of the page. `prompt.position` accepts `top` or `bottom` only. * **`modal`** — a window centered on the page. `prompt.position` is always `center`. Check the list above for the layout you set before picking a position. In the init snippet, a position the current layout doesn’t support is silently replaced with the layout’s default. The Control Panel form rejects the combination instead. ### When to show the prompt `display.trigger` controls what opens the prompt: * **`auto`** — opens automatically `display.delay` seconds after the page loads, then again every `display.cappingInterval` hours if the visitor hasn’t decided, up to `display.cappingCount` times total (`0` means no limit). * **`launcher`** — never opens on a timer. Set this alongside `launcher.enabled: true` so a bell click can open it. The manual API still opens it regardless of this setting or of whether the bell is on. * **`manual`** — opens only through `Pushwoosh.moduleRegistry.subscriptionWidget`, described below. `display.delay`, `display.cappingCount`, and `display.cappingInterval` only apply to the `auto` trigger. ### The launcher bell `launcher.enabled` shows a floating bell to a visitor who can still act on the ask (not yet subscribed, or blocked), independent of the prompt and of `display.trigger`. The bell stays on screen while the prompt is open. `launcher.position` places it in one of the four corners. `launcher.offset` sets its distance from the edges, in pixels. A bell click’s effect depends on the prompt. If `prompt.enabled` is `true`, the click toggles it: opens it, or closes it if it’s already open. If the prompt is off, the click asks the browser for permission directly, the same as `Pushwoosh.subscribe()` — unless the visitor already blocked notifications, when the click does nothing. `launcher.width` and `launcher.height` set the bell’s size in pixels (16–200, default 48 each). `launcher.imageUrl` draws a picture instead of the round bell button. `launcher.background` and `launcher.iconColor` set the round button’s fill and glyph color. Left unset, `launcher.background` defaults to the palette’s accent color, and `launcher.iconColor` to a glyph color computed for contrast against it. ### Texts and colors `texts.blockedTitle`, `texts.blockedMessage`, and `texts.closeText` are shown instead of the ask once the browser reports the visitor blocked notifications — `texts.acceptText` and `texts.declineText` never appear in that state. `texts.launcherLabel` and `texts.launcherBlockedLabel` are the bell’s tooltip in each state. Every text field accepts up to 300 characters. `colors.accent`, `colors.background`, `colors.text`, and `colors.textSecondary` take a hex value or a CSS color name. The Control Panel forms validate each of these, and `launcher.background`/`launcher.iconColor`, this way, up to 64 characters. The init snippet accepts any non-empty string for `colors.*` with no format or length check. For `launcher.background`/`launcher.iconColor` it checks neither format, but silently cuts the value to 64 characters. Note A dark-mode palette, `launcher.zIndex`, and a custom `fontFamily` are available as additional init-param-only fields. They aren’t exposed in the Control Panel forms described below. ## Managing it in the Control Panel Panel-managed applications only The Control Panel cards are available only for applications with the Web platform in **panel-managed** mode (see [Web Push SDK 3.0](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/)). For a snippet-configured application, set `subscriptionWidget` in the init snippet as shown above. On a panel-managed application, go to **Settings → Configure platforms → Web**. The prompt and the bell are two separate cards there, each with its own **On**/**Off** badge and **Configure** button: * **Subscription invitation** edits the prompt: **Invitation** (layout, position, icon), **When to show** (trigger, delay, capping), **Texts**, and **Colors**. * **Subscription bell** edits the launcher: position, image URL, width/height, background and icon color, and its own tooltip texts. Each card’s **Configure** button opens a live preview next to the form. The preview draws nothing while that card’s own switch is off. Once it’s on, a dropdown above the preview switches between the undecided-visitor ask and the blocked-visitor explanation. Saving a card replaces only its half of the panel’s copy of the widget config — the other card’s settings, and the WebSDK init snippet on your site, are untouched. While an application is panel-managed, values saved in either card override the same keys in the site’s init snippet. ## 3. Control the prompt from your code Once the widget bundle has loaded, it publishes an API at `Pushwoosh.moduleRegistry.subscriptionWidget`: ```javascript Pushwoosh.moduleRegistry.subscriptionWidget.show(); // Opens the prompt Pushwoosh.moduleRegistry.subscriptionWidget.hide(); // Closes the prompt Pushwoosh.moduleRegistry.subscriptionWidget.toggle(); // Toggles the current state Pushwoosh.moduleRegistry.subscriptionWidget.toggle(true); // Opens the prompt Pushwoosh.moduleRegistry.subscriptionWidget.isVisible(); // Returns whether the prompt is open ``` `show()` and `toggle()` have no effect while `prompt.enabled` is `false`, and no effect for a visitor who already subscribed. Opening the prompt dispatches a `show-subscription-widget` event. Closing it dispatches `hide-subscription-widget`: ```javascript Pushwoosh.addEventHandler('show-subscription-widget', function() { console.log('Triggered event: show-subscription-widget'); }); Pushwoosh.addEventHandler('hide-subscription-widget', function() { console.log('Triggered event: hide-subscription-widget'); }); ``` ## Migrating from the older widgets The push subscription button, the Custom Subscription Popup, and the Subscription Prompt keep working as documented on their own pages as long as the site’s `subscriptionWidget.prompt.enabled` and `subscriptionWidget.launcher.enabled` both stay unset or `false`. Turning either one on stops all three from loading, on both a snippet-configured and a panel-managed application. Caution An earlier version of the widget used a single top-level `subscriptionWidget.enable` switch instead of separate `prompt.enabled`/`launcher.enabled`. That shape is no longer read. A snippet still setting only `enable: true` now turns the widget off entirely: neither the prompt nor the bell draws anything, the WebSDK logs a console hint naming the two fields to set instead, and the three older widgets load as if `subscriptionWidget` were never set. Switching a panel-managed application to the new widget does not carry over the settings of whichever old widget it replaces either — each card starts from its own defaults. --- # Usage with npm **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/usage-with-npm/ How to install and use Pushwoosh Web Push Notifications SDK via npm --- ## Installing Pushwoosh Web Push SDK via npm Here’s how to install and use the Pushwoosh Web Push Notifications SDK in your project using npm. This guide assumes you have a basic understanding of npm and JavaScript module imports. ### Install the SDK: Install Pushwoosh Web Push Notifications SDK ```bash npm install web-push-notifications ``` Important Note on SDK Versioning (v3.45.0+) Starting from version **3.45.0**, the Pushwoosh Web SDK introduced a modular approach for importing the core SDK and its UI widgets. * **Separate Imports:** You now need to import the main `Pushwoosh` class and any desired widgets (like `PWSubscriptionButtonWidget`, `PWSubscribePopupWidget`, etc.) separately. Prior to this version, all components were typically bundled into a single import. * **ES2020 Compilation:** The SDK is compiled to the ES2020 JavaScript specification from this version onwards. Please ensure your build environment and target browsers support ES2020 features. ### Service Worker Setup When using the SDK via npm, the service worker file is located at: ```plaintext node_modules/web-push-notifications/service-worker.js ``` **Important:** During your build or deployment process, you must copy this service worker file to the root of your website. This is crucial for push notifications to work correctly. For example, in a webpack project, you can use `copy-webpack-plugin`: ```javascript const CopyWebpackPlugin = require('copy-webpack-plugin'); module.exports = { // ...other webpack config plugins: [ new CopyWebpackPlugin({ patterns: [ { from: 'node_modules/web-push-notifications/service-worker.js', to: 'service-worker.js' // Copies to output directory root } ] }) ] }; ``` If you’re using Vite, you can use the `vite-plugin-static-copy` plugin: vite.config.js ```javascript import { defineConfig } from 'vite'; import { viteStaticCopy } from 'vite-plugin-static-copy' export default defineConfig({ // ...other vite config plugins: [ // ...other plugins viteStaticCopy({ targets: [ { src: 'node_modules/web-push-notifications/service-worker.js', dest: '' // Copies to build output root } ] }) ] }); ``` For other build systems, use their respective file copying mechanisms to ensure the service worker is available at your website’s root. ### Built-in Widgets The Pushwoosh Web SDK comes with four built-in UI widgets to enhance user interaction and streamline the subscription process. While the `PWSubscriptionPromptWidget` is primarily configured through the Pushwoosh Control Panel and browser settings, the other three widgets (`PWSubscriptionButtonWidget`, `PWSubscribePopupWidget`, and `PWInboxWidget`) offer detailed configuration options directly within the `init` method of the SDK. Here’s an overview of each widget: 1. **Subscription Prompt (`PWSubscriptionPromptWidget`)** This widget is responsible for displaying the browser’s native prompt to ask users for permission to send push notifications. Its behavior is largely governed by browser standards and settings configured in your Pushwoosh Control Panel. It does not have specific configuration parameters within the `pushwoosh.push(['init', {...}])` call. 2. **Subscription Button (`PWSubscriptionButtonWidget`)** This widget adds a floating button (often a bell icon) to your site, allowing users to easily subscribe to or manage their notification preferences. * **Configuration:** Settings for this widget are managed under the `subscribeWidget` object in the `init` call. * **Learn more:** [Push Subscription Button](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/push-subscription-button/) 3. **Custom Subscription Popup (`PWSubscribePopupWidget`)** A customizable pop-up that provides a more engaging way to ask for notification permissions before showing the native browser prompt. * **Configuration:** Customize its appearance and behavior using the `subscribePopup` object in the `init` call. * **Learn more:** [Custom Subscription Popup](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/) 4. **Inbox Widget (`PWInboxWidget`)** This widget integrates an in-app message center, allowing users to view a history of rich push notifications. * **Configuration:** Managed via the `inboxWidget` object in the `init` call. * **Learn more:** [Web Inbox Widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-inbox-widget/) You can then initialize and run these widgets as shown in the examples below: ### Import and Initialize Import and Initialize Pushwoosh ```javascript import { Pushwoosh } from 'web-push-notifications'; import { PWSubscriptionPromptWidget } from 'web-push-notifications/widget-subscription-prompt'; import { PWSubscriptionButtonWidget } from 'web-push-notifications/widget-subscription-button'; import { PWSubscribePopupWidget } from 'web-push-notifications/widget-subscribe-popup'; import { PWInboxWidget } from 'web-push-notifications/widget-inbox'; const pushwoosh = new Pushwoosh(); pushwoosh.push(['init', { applicationCode: 'XXXXX-XXXXX', // your Pushwoosh Application Code apiToken: 'XXXXXXX', // Device API Token defaultNotificationTitle: 'Pushwoosh', defaultNotificationImage: 'https://yoursite.com/img/logo-medium.png', serviceWorkerUrl: '/service-worker.js', // path to your copied service worker // Custom Subscription Popup settings subscribePopup: { enable: true // Enable the subscription popup // other options can be added here }, // Push subscription button settings subscribeWidget: { enable: true // Enable the subscription button widget // other options can be added here }, // Inbox widget settings inboxWidget: { enable: true // Enable the inbox widget // other options can be added here } }]); pushwoosh.push(async () => { try { const widget = new PWSubscriptionPromptWidget(pushwoosh); await widget.run(); } catch (error) { console.error('PWSubscriptionPromptWidget initialization failed:', error); } }); pushwoosh.push(async () => { try { const widget = new PWSubscriptionButtonWidget(pushwoosh); await widget.run(); } catch (error) { console.error('PWSubscriptionButtonWidget initialization failed:', error); } }); pushwoosh.push(async () => { try { const widget = new PWSubscribePopupWidget(pushwoosh); await widget.run(); } catch (error) { console.error('PWSubscribePopupWidget initialization failed:', error); } }); pushwoosh.push(async () => { try { const widget = new PWInboxWidget(pushwoosh); await widget.run(); } catch (error) { console.error('PWInboxWidget initialization failed:', error); } }); ``` ## Links See an example application with React and Vite: --- # Web inbox widget **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-inbox-widget/ How to integrate the out-of-the-box website widget for inbox messages --- [Web Message Inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) being integrated into your website increases conversions drastically. A ready-to-use **Web Inbox Widget** is out of the box subscribed to all methods needed to communicate with users. Just integrate the widget into your website and customize its appearance to fit your brand. Setting `inboxWidget.enable: true` registers a visitor’s device for Inbox delivery even when the visitor hasn’t granted push notification permission, so it can reach visitors who never subscribed to push. Important Note on SDK Versioning (v3.66.0+) Starting from version **3.66.0**, a visitor’s device is tracked by a hardware ID tied to their browser before the visitor logs in. If the visitor clears their site data, switches browsers, or switches devices before calling `registerUser`, that tracking breaks. Their prior message history is lost and does not carry over even after the visitor logs in. ## Prerequisites To integrate Message Inbox for websites, please follow the [guide](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/). ## Integrating widget When initializing Pushwoosh WebSDK, add the `inboxWidget` parameter: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox' } }]); ``` ### Trigger To render the widget, you should specify a **trigger element** for the widget. Any DOM-element on the page could be the trigger. Specify its ID as “pwInbox” or assign any custom ID to the `triggerId` parameter when initializing SDK.\ When WebSDK is initialized, the unread messages badge displays on the trigger element. Once the trigger element is tapped, the widget appears on the page. ### Positioning By default, the widget is located in the opposite direction from the screen border closest to the trigger element.\ You can set the widget’s position through configuration params: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', position: 'top' } }]); ``` By default, the `inboxWidget` should be placed before the closing tag.\ Render the widget anywhere on the page as shown in the example below: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', appendTo: '.widget-parent-element-class' } }]); ``` Search for a parent element is performed by querySelector, so it needs clear specification of a class, id, or element. ## Customizing widget ### Customizing texts To customize widget text, add `emptyInboxTitle` and `emptyInboxText` parameters: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', title: 'Inbox', // widget title emptyInboxTitle: 'You\'re all caught up', // empty inbox widget title emptyInboxText: 'There are no new messages. Stay tuned!' // empty inbox widget text } }]); ``` The result looks like this: ### Customizing appearance To customize the widget’s **appearance**, use the following parameters: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', bgColor: '#ffffff', // widget background borderColor: 'transparent', // widget border color borderRadius: 4, // widget border radius (px) widgetWidth: 350, // widget width (px) zIndex: 100 // z-index } }]); ``` **Unread messages count badge** can be customized as follows: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', badgeBgColor: '#ff4c00', // badge color badgeTextColor: '#ffffff' // badge text color } }]); ``` To customize **text properties**, add the following code: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', textColor: '#333333', // text color fontFamily: 'Helvetica, Arial, sans-serif', // font messageTitleColor: '#7a7a7a', // messages title color timeTextColor: '#c4c4c4', // timestamp text color emptyInboxTitleColor: '#333333', // empty inbox title color emptyInboxTextColor: '#7a7a7a' // empty inbox text color } }]); ``` Lastly, you can specify the **icon for empty inbox**: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', emptyInboxIconUrl: 'https://pushon.pushwoosh.com/images/icon-empty-inbox.png' // URL path to an icon } }]); ``` ### Customizing cell layouts The widget draws a message as one of four **cell layouts** — `classic`, `banner`, `captioned`, or `carousel` — the same layouts the campaign preview in the control panel shows. A layout that can’t render with what the message carries degrades to `classic`: `banner` needs a hero image, `captioned` needs a hero image plus both a title and a body, and `carousel` needs at least one slide plus both a title and a body. To customize the layouts’ appearance, use the following parameters: Example ```txt Pushwoosh.push(['init', { ..., inboxWidget: { enable: true, triggerId: 'pwInbox', unreadDotColor: '#ff4c00', // unread dot on the classic avatar avatarBgColor: '#e6e6e6', // classic avatar background avatarTextColor: '#333333', // classic avatar initial color avatarSize: 52, // classic avatar size (px) mediaRadius: 8, // corner radius of banner/captioned/carousel media (px) slideCaptionColor: '#ffffff', // carousel slide caption text color slideScrimColor: '#000000', // carousel slide caption background scrim pagerDotActiveColor: '#ff4c00', // carousel active page dot pagerDotColor: '#c4c4c4' // carousel inactive page dots } }]); ``` Restoring the pre-3.75 classic cell Set `inboxWidget.legacyClassicCell: true` to keep the classic cell’s previous look — a 40px icon column instead of the 52px rounded avatar, no unread dot, and an unclamped body. The `banner`, `captioned`, and `carousel` layouts are unaffected by this option. ## Managing it in the Control Panel Requirements * The site loads the Web SDK from the Pushwoosh CDN, as the card says. The card works regardless of the **Settings source**, the init snippet on your site or this page (see [Web Push SDK 3.0](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#configuration)), but the SDK version it needs depends on that source: * **The init snippet on your site:** Web SDK 3.93.1 or later. An older SDK ignores the card and reads `inboxWidget` from the snippet only. * **This page:** Web SDK 3.92 or later for the card’s values, and 3.93.0 or later for the npm turn-off described below. * The page has the element that opens the inbox, `#pwInbox` by default. If it’s missing, the inbox doesn’t open, and the browser console shows an `Error during Pushwoosh initialization` error that names the missing element and says where to change its ID. * On a site that installs the SDK from npm, the card can turn the widget off but not on, since the site constructs the widget itself. To set up the widget from the Control Panel: 1. Go to **Settings → Configure platforms → Web**. 2. On the **Inbox widget** card, click **Configure**. ![Inbox widget card on the Web platform settings page with the ON badge, the Configure button, and the Return to snippet button](https://docs.pushwoosh.com/web-push-notifications-web-inbox-widget-3.webp) 3. Turn on **Show the inbox on the site**. 4. Fill in the fields you want to manage from the panel, and leave the rest empty. An empty field isn’t saved, so the snippet’s value, or the SDK default, still applies. Leave **ID of the element that opens the inbox** and **Insert into (CSS selector)** empty to keep `triggerId` and `appendTo` in the snippet next to the markup they point to. 5. Check the result in the preview. Switch between **With messages** and **No messages** to see both states. ![Inbox widget form with the Show the inbox on the site switch on, Placement, Texts, and Appearance fields, and a preview of two sample messages](https://docs.pushwoosh.com/web-push-notifications-web-inbox-widget-4.webp) 6. Save the form. **Width, px** takes a whole number from 200 to 1200, and **Corner radius, px** from 0 to 64. Any other value shows an error under the field and isn’t saved. ### How the card works with the snippet Values saved in the card override the same `inboxWidget` keys in the init snippet, key by key. Each field sets one key: | Section | Field | `inboxWidget` key | | ---------- | --------------------------------------------- | -------------------------------------------------- | | Placement | ID of the element that opens the inbox | `triggerId` | | Placement | Opens | `position` | | Placement | Insert into (CSS selector) | `appendTo` | | Texts | Widget title | `title` | | Texts | Title when there are no messages | `emptyInboxTitle` | | Texts | Text when there are no messages | `emptyInboxText` | | Texts | Image when there are no messages (URL) | `emptyInboxIconUrl` | | Appearance | Width, px (200–1200) | `widgetWidth` | | Appearance | Corner radius, px (0–64) | `borderRadius` | | Appearance | Font | `fontFamily` | | Appearance | Background, Text, Border | `bgColor`, `textColor`, `borderColor` | | Appearance | Counter background, Counter text, Unread mark | `badgeBgColor`, `badgeTextColor`, `unreadDotColor` | Keys the card doesn’t have, such as the cell layout parameters and `legacyClassicCell`, are always read from the snippet. The **Show the inbox on the site** switch works the same way. Until it’s saved in the panel, the card’s badge reads **Set in the snippet**, and the snippet’s `enable` decides whether the widget shows. Once you save the switch, the badge reads **On** or **Off**, and the card overrides the snippet: * **Off** hides the widget even if the snippet sets `enable: true`. * **On** shows the widget even if the snippet has no `inboxWidget` at all. Like `enable: true`, it registers visitors for Inbox delivery without push permission. Saved changes reach the site within a minute, on the next page load. To hand the widget back to the snippet, click **Return to snippet** on the card and confirm. The button appears once anything is saved in the card. It deletes every inbox setting saved in the panel, and the site uses the `inboxWidget` settings of its snippet again. ## API To open or close the widget, use the following API method: Example ```txt Pushwoosh.pwinboxWidget.toggle(open?: boolean); ``` Opening the widget refreshes it from the server, at most once every 30 seconds — a standalone App Inbox message rides a channel with no push, so nothing else announces one between page loads. --- # Web push SDK 3.0 **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/ How to integrate and configure Web Push SDK for HTTPS websites --- Note #### Prerequisites To proceed with the integration of Web Push SDK to your HTTPS website, you should do the following: 1. Locate your Pushwoosh Application Code. 2. Chrome and Firefox: locate your Firebase API key and Sender ID. To do so, please follow steps 1-3 of [Chrome and Firefox Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/chrome-configuration/) guide. 3. Safari, if your application has the [Safari Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/#safari-configuration-macos-101412) section: locate your [Website Push ID](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/safari-configuration/#1-registering-with-apple). 4. Download [Pushwoosh Web Push SDK](https://cdn.pushwoosh.com/webpush/v3/PushwooshWebSDKFiles.zip). Caution * Chrome pushes will not work with **self-signed** certificates (https/ssl). You’ll need SSL certificate signed by trusted Authority. * Push notifications don’t work in both Incognito and Guest mode. * Auto subscription is not available for Safari. ## Integration Using npm? If you prefer using npm for package management, you can also install and integrate the Pushwoosh Web SDK via npm. Please refer to our [Usage with npm guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/usage-with-npm/) for detailed instructions. [**Integration sample on GitHub**](https://github.com/Pushwoosh/web-push-notifications-sample) ### Get Pushwoosh Web Push SDK and unzip it. You should have the following files: Download [Pushwoosh Web Push SDK](https://cdn.pushwoosh.com/webpush/v3/PushwooshWebSDKFiles.zip) if you haven’t already. * **pushwoosh-service-worker.js** You can also download the service worker file from the Control Panel: 1. Go to **Settings → Configure platforms → Web push notifications**. 2. On the **Web SDK configuration** card, click **Get the code**. See [Configuration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#configuration) for what else this card does. 3. Click **Download service worker**. The downloaded `pushwoosh-service-worker.js` holds a single line that loads the service worker from the Pushwoosh CDN: ```js importScripts('https://cdn.pushwoosh.com/webpush/v3/pushwoosh-service-worker.js' + self.location.search); ``` ![Web SDK integration code dialog with the Download service worker button next to Copy the code](https://docs.pushwoosh.com/web-push-notifications-web-push-sdk-3.0-4.webp) The **Get the code** button is shown only to users who can manage API access tokens, because the same dialog generates the init snippet with a Device API token. If you don’t see it, use `pushwoosh-service-worker.js` from the SDK archive above, or ask your account admin for [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) permissions. ### Place all these files to top-level root of your website directory. Note Make sure the following URLs are publicly accessible: * ### Initialize the SDK: 1. Include the SDK from our CDN *asynchronously*. Google Tag Manager supported Click [here](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#installing-from-google-tag-manager) if you are using Google Tag Manager. ```txt ``` 2. Initialize the Web Push SDK and make sure to queue the initialization until the moment the SDK is fully loaded. Note To initialize the Web Push SDK, you must include a [Device Api token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) in the `apiToken` parameter. **Important:** Make sure the token has access to the correct application in your Pushwoosh Control Panel. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) ```html ``` #### Web popups [Web popup campaigns](https://docs.pushwoosh.com/product/messaging-channels/web-popups/#start-working-with-web-popups) load by default — no `webPopups` entry is required in your `init` object. Add one only to opt out or change defaults: ```javascript webPopups: { // enable: false, // optional, opt out entirely: the bundle is never loaded autoShow: true, // optional, set to false to load popups without displaying them automatically — see WebPopups methods below colorScheme: 'auto', // optional, 'auto' (default) | 'light' | 'dark' }, ``` Web popup campaigns display content you configure in the Control Panel, such as promos, announcements, or lead capture forms, over the page or, for Inline campaigns, inside a page element. Unlike the [custom subscription popup](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/) (`subscribePopup`), which only handles web push opt-in, Web popup campaigns can display any content you configure. For setup in the Control Panel, see [Understanding Web popups](https://docs.pushwoosh.com/product/messaging-channels/web-popups/). To stop web popups on a site without changing its snippet, turn them off in the Control Panel: 1. Go to **Settings → Configure platforms → Web push notifications** and find the **Web popups** card. 2. Click **Turn off**. ![Web popups card on the Web platform settings page with the Set in the snippet badge and the Turn off button](https://docs.pushwoosh.com/web-push-notifications-web-push-sdk-3.0-3.webp) The badge changes from **Set in the snippet** to **Off**. From the next page load, within a minute, the SDK doesn’t load popups even if the snippet sets `webPopups.enable: true`. `autoShow` and `colorScheme` still come from the snippet. To let the snippet decide again, click **Return to snippet** on the same card. The card works regardless of the **Settings source**, but the SDK version it needs depends on that source: * **The init snippet on your site:** Web SDK 3.93.1 or later. An older SDK ignores the switch and keeps loading popups as the snippet says. * **This page:** Web SDK 3.93.0 or later. `colorScheme` picks which palette a popup with [dark mode](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode) configured renders with: * `'auto'` (default): follows the visitor’s OS-level color scheme (`prefers-color-scheme`). * `'light'` or `'dark'`: always renders that palette, regardless of the visitor’s OS setting. A popup without dark mode configured in the editor always renders light — `colorScheme` has no effect on it. For programmatic control, see [WebPopups methods](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#webpopups-methods) and [Web popup events](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#web-popup-events). ##### Inline campaigns and web forms * **Inline campaigns** need Web SDK 3.97.0 or later from the CDN, or 3.98.0 or later from the `web-push-notifications` npm package. An older SDK shows an Inline campaign as a popup over the page. The SDK renders an Inline campaign inside the first element that matches the campaign’s CSS selector. If no element matches, the campaign is skipped. The SDK doesn’t watch for elements added later: once the element is on the page, call `Pushwoosh.moduleRegistry.webPopups.show('CAMPAIGN_CODE')`, which resolves `false` if there is still no match. * **[Web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/)** need Web SDK 3.99.0 or later. The SDK validates the fields, sends the values to Pushwoosh under the code of the campaign that shows the form, and opens the form’s Success page once Pushwoosh accepts them. An older SDK shows the form content without its fields. * A web form is shown only by a campaign. `show()` with the code of a form content, not a campaign, resolves `false` and logs an error to the console. The SDK doesn’t fire a JavaScript event for form submissions. Pushwoosh records them as events of your project instead, with the campaign code in the `form_code` attribute: | Event | When it’s recorded | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `PW_PopupFormSubmit` | Pushwoosh accepted a submission. | | `PW_PopupFormSubmitError` | Pushwoosh rejected a submission to an active campaign of this project. Requests without a campaign code, or with the code of a missing, inactive, or foreign campaign or of a campaign without a web form, are rejected without this event. The `reason` attribute is `invalid_email`, `bounced`, `invalid_fields`, `no_verified_domain`, `tariff`, or `server_error`. | | `PW_PopupFormConfirm` | A visitor confirmed the email by the double opt-in link. | #### Subscription prompt To prompt your users to subscribe for push notifications, use the [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/), a prompt and a launcher bell you configure together. It replaces the older [push subscription button](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/push-subscription-button/) and [Custom Subscription Popup](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/), which stay in place for a site already using them but aren’t the recommended starting point for a new integration. ## Configuration To finish implementing push notifications into your website, you need to configure web platforms in your Pushwoosh Control Panel following our step-by-step guides: * [Chrome Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/chrome-configuration/) * [Firefox Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/chrome-configuration/) * [Safari Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/safari-configuration/) Note To get FCM sender ID and API Key, please follow steps 1-3 of the [Android configuration guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration/). The same configuration screen also includes a **Web SDK configuration** card. Its **Get the code** button also lets you [download the service worker file](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#get-pushwoosh-web-push-sdk-and-unzip-it-you-should-have-the-following-files). Managing SDK settings from the Control Panel The **Web SDK configuration** card lets you set `autoSubscribe`, `defaultNotificationTitle`, `defaultNotificationImage`, `safariWebsitePushID`, and `subscribeWidget` from the Control Panel instead of the init snippet. Its **Settings source** field starts at **the init snippet on your site**. Click **Move the settings here** to switch the source to **this page**. The SDK then reads these five keys from Pushwoosh and ignores the same keys in your `init` object. Moving the settings is a one-off step, not a toggle you can flip back and forth. On an application already on the unified web platform (where Safari shares the same driver as Chrome and Firefox), the Control Panel no longer offers a way back to the snippet. Reverting would re-register every device under its own browser platform and lose its tags. The same page has cards for the [Web inbox widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-inbox-widget/#managing-it-in-the-control-panel) and [Web popups](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#web-popups) regardless of the **Settings source**. While the source is **the init snippet on your site**, these two cards are the only settings Pushwoosh passes to the SDK. The [Subscription widget](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/#managing-it-in-the-control-panel) cards appear only on a panel-managed application. ## Registering service worker in a different scope Sometimes you can’t place the service worker file in a root directory of a website but in a subdirectory. In this case, modify the configuration (**step 4.3**) by adding a parameter `serviceWorkerUrl: “/push-notifications/pushwoosh-service-worker.js”` where `/push-notifications/pushwoosh-service-worker.js` is the path to `pushwoosh-service-worker.js` file. ## Event handlers In Pushwoosh Web SDK 3.0 you can subscribe to certain events to track them**,** or unsubscribe from events if don’t need tracking them anymore. To track the Web SDK 3.0 load, fire the `onLoad` event as follows: ```javascript // Load Event Pushwoosh.push(['onLoad', (api) => { console.log('Pushwoosh load!'); }]); ``` To track the correct Web SDK initialization, fire the `onReady` event: ```javascript // Ready Event Pushwoosh.push((api) => { console.log('Pushwoosh ready!'); }); ``` To subscribe to or unsubscribe from any of the SDK events, use the handlers after the SDK load: ```javascript Pushwoosh.push(['onLoad', (api) => { function onEventNameHandler() { console.log('Triggered event: event-name!'); } // To subscribe to an event: Pushwoosh.addEventHandler('event-name', onEventNameHandler) // To unsubscribe from an event: Pushwoosh.removeEventHandler('event-name', onEventNameHandler) }]); ``` ### SDK events #### Subscribe event Executed after a user agrees to receive push notifications. ```javascript Pushwoosh.push(['onLoad', (api) => { Pushwoosh.addEventHandler('subscribe', (payload) => { console.log('Triggered event: subscribe'); }); }]); ``` #### Unsubscribe event Executed after a device is unregistered from notifications. ```javascript Pushwoosh.push(['onLoad', (api) => { Pushwoosh.addEventHandler('unsubscribe', (payload) => { console.log('Triggered event: unsubscribe'); }); }]); ``` #### Subscription widget events Track displaying of a Subscription Prompt widget. ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed on displaying of the Subscription Prompt widget Pushwoosh.addEventHandler('show-subscription-widget', (payload) => { console.log('Triggered event: show-subscription-widget'); }); // Executed on hiding of the Subscription Prompt widget Pushwoosh.addEventHandler('hide-subscription-widget', (payload) => { console.log('Triggered event: hide-subscription-widget'); }); }]); ``` #### Notification permission dialog events Track displaying of native subscription dialog. ```javascript Pushwoosh.push(['onLoad', function (api) { // Executed on permission dialog displaying Pushwoosh.addEventHandler('show-notification-permission-dialog', (payload) => { console.log('Triggered event: show-notification-permission-dialog'); }); // Executed on hiding the permission dialog with one of three possible statuses: // 1. default - the dialog is closed // 2. granted - permission is granted // 3. denied - permission is denied Pushwoosh.addEventHandler('hide-notification-permission-dialog', (payload) => { console.log('Triggered event: hide-notification-permission-dialog', payload.permission); }); }]); ``` #### Permission events Check the push notifications permission’s status on SDK initialization; track the update of this status whenever it takes place. ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed during the SDK initialization if 'autoSubscribe: false' or/and if a user ignores a push notification prompt. Pushwoosh.addEventHandler('permission-default', (payload) => { console.log('Triggered event: permission-default'); }); // Executed during the SDK initialization if notifications are blocked or once a user blocks push notifications. Pushwoosh.addEventHandler('permission-denied', (payload) => { console.log('Triggered event: permission-denied'); }); // Executed during the SDK initialization if notifications are allowed or once a user allows push notifications. Pushwoosh.addEventHandler('permission-granted', (payload) => { console.log('Triggered event: permission-granted'); }); }]); ``` #### Receive push event Track push delivery to a device. ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed when a push notification is displayed. Pushwoosh.addEventHandler('receive-push', (payload) => { console.log('Triggered event: receive-push', payload.notification); }); }]); ``` #### Notification events Track whether a push notification is opened or closed by a user. ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed when a user clicks on notification. Pushwoosh.addEventHandler('open-notification', (payload) => { console.log('Triggered event: open-notification', payload.notification); }); // Executed when a user closes a push notification. Pushwoosh.addEventHandler('hide-notification', (payload) => { console.log('Triggered event: hide-notification', payload.notification); }); }]); ``` #### Inbox events Track notifications sent to Inbox. ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed by ServiceWorker after the Inbox Message is received and saved to indexedDB. Pushwoosh.addEventHandler('receive-inbox-message', (payload) => { console.log('Triggered event: receive-inbox-message', payload.message); }); // Executed after the Inbox is updated automatically while the page is loading. Pushwoosh.addEventHandler('update-inbox-messages', (payload) => { console.log('Triggered event: receive-inbox-message', payload.messages); }); }]); ``` #### Custom subscription popup events For details about handling custom subscription popup events, please refer to the [Custom Subscription Popup Events Guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/custom-subscription-popup/#custom-subscription-popup-events). #### Web popup events ```javascript Pushwoosh.push(['onLoad', (api) => { // Executed once web popups have loaded and the automatic display pipeline has run for the current page Pushwoosh.addEventHandler('web-popups-ready', () => { console.log('Triggered event: web-popups-ready'); }); // Executed when a web popup is shown, automatically or via webPopups.show() Pushwoosh.addEventHandler('show-web-popup', (payload) => { console.log('Triggered event: show-web-popup', payload.code, payload.trigger); // trigger: 'auto' | 'api' }); // Executed when a web popup is hidden Pushwoosh.addEventHandler('hide-web-popup', (payload) => { console.log('Triggered event: hide-web-popup', payload.code, payload.reason); // reason: 'user' | 'api' | 'preempted' }); }]); ``` ## API After the Web Push SDK is initialized, you can make the following calls to Pushwoosh API. All the methods return [**Promise**](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Promise) objects. ```javascript Pushwoosh.push((api) => { // Set tags for a user api.setTags({ 'Tag Name 1': 'value1', 'Tag Name 2': 'value2' }); // Get tags for a user from server api.getTags(); // Register user ID api.registerUser('user123'); // Register user email api.registerEmail('user@example.com'); // Register SMS number api.registerSmsNumber('+15551234567'); // Register WhatsApp number api.registerWhatsappNumber('+1234567890'); // Post an Event api.postEvent('myEventName', {attributeName: 'attributeValue'}); //Unregister from notifications api.unregisterDevice(); // Set the device language (overrides the value in the "Language" Tag) api.setLanguage('es'); // Alternatively Multi-register user with devices and channels api.multiRegisterDevice({ user_id: 'user123', email: 'user@example.com', sms_phone_number: '+1234567890', tags: { 'UserType': { operation: TTagOperationSet, value: 'Premium' }, 'Interests': { operation: TTagOperationAppend, values: ['sports', 'technology'] } } }); }); ``` ### multiRegisterDevice Enhanced registration method that allows registering a user profile with multiple devices and messaging channels in a single API call. This method is particularly useful for cross-platform applications or when implementing omnichannel messaging strategies. ```javascript Pushwoosh.push((api) => { api.multiRegisterDevice({ user_id: 'user123', // Optional: User identifier email: 'user@example.com', // Optional: Email for email messaging sms_phone_number: '+1234567890', // Optional: SMS phone number (E.164 format) whatsapp_phone_number: '+1234567890', // Optional: WhatsApp number (E.164 format) kakao_phone_number: '+1234567890', // Optional: KakaoTalk number (E.164 format) language: 'en', // Optional: Language code (ISO 639-1) timezone: 'America/New_York', // Optional: Timezone identifier city: 'New York', // Optional: City for targeting country: 'US', // Optional: Country for targeting state: 'NY', // Optional: State for targeting tags: { // Optional: Tag values with operations 'UserType': { operation: TTagOperationSet, // Set tag value (0) value: 'Premium' }, 'Interests': { operation: TTagOperationAppend, // Append to tag value (1) values: ['sports', 'technology'] }, 'LoginCount': { operation: TTagOperationIncrement, // Increment tag value (3) value: '1' } }, push_devices: [ // Optional: Array of push devices { hwid: 'web-device-456', platform: TPlatformChrome, // Chrome platform (11) push_token: 'fcm-token-here', app_version: '2.1.0', platformData: { public_key: 'web-push-public-key', auth_token: 'web-push-auth-token', browser: 'chrome' } } ] }) .then((response) => { console.log('Multi-registration successful:', response); }) .catch((error) => { console.error('Multi-registration failed:', error); }); }); ``` **Platform Types:** * `TPlatformSafari` (10): Safari on the Apple Website Push ID path. This covers: * Safari on macOS 12 and earlier. * Any Safari that already has a subscription through the Website Push ID. Existing subscribers aren’t migrated to 11. * A new Safari subscriber on an application without web push keys from [Configure web push for all browsers (except older Safari versions)](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/#configure-web-push-for-all-browsers-except-older-safari-versions), unless the application is on the unified web platform. A new Safari subscriber on macOS Ventura and later registers as `TPlatformChrome` (11) through the service worker when the application has these web push keys or is on the unified web platform. * `TPlatformChrome` (11): Chrome platform * `TPlatformFirefox` (12): Firefox platform **Tag Operation Types:** * `TTagOperationSet` (0): Set tag value (replace existing value) * `TTagOperationAppend` (1): Append to tag value (add to list) * `TTagOperationRemove` (2): Remove tag value (remove from list) * `TTagOperationIncrement` (3): Increment tag value (numeric increment) **Benefits:** * **Single API call**: Register multiple devices and channels at once * **Atomic operation**: All registrations succeed or fail together * **User-centric**: Associates all devices with a single user profile * **Advanced tagging**: Supports complex tag operations * **Cross-platform**: Handle multiple platforms simultaneously Example of sending Tags to Pushwoosh: ```javascript Pushwoosh.push((api) => { var myCustomTags = { 'Tag 1': 123, 'Tag 2': 'some string' }; api.setTags(myCustomTags) .then((res) => { var skipped = res && res.skipped || []; if (!skipped.length) { console.log('success'); } else { console.warn('skipped tags:', skipped); } }) .catch((err) => { console.error('setTags error:', err); }); }); ``` ### Increment Tag value To i**ncrement a value** of a Number Tag, use the `operation` parameter with the ‘increment’ value as follows: ```javascript Pushwoosh.push((api) => { api.setTags({ 'Tag 1': { operation: 'increment', value: 1 } }) }); ``` ### Append Tag values To **append new values** to the existing List Tag, use the `operation` parameter with the ‘append’ value as follows: ```javascript Pushwoosh.push((api) => { api.setTags({ 'Tag 3': { operation: 'append', value: ['Value3'] } }) }); ``` ### Remove Tag value To **remove a value** from a List Tag, use the `operation` parameter with the ‘remove’ value as follows: ```javascript Pushwoosh.push((api) =>{ api.setTags({ 'Tag 3': { operation: 'remove', value: ['Value2'] } }) }); ``` ## Public methods Caution Please note that **auto subscription is not available for Safari users**. Please consider subscribing Safari users to push notifications by calling the `Pushwoosh.subscribe()` method. **Pushwoosh.subscribe()** This method is used to request a user’s permission for push notifications. If a user is already subscribed, the method will stop executing. If a user hasn’t subscribed for pushes yet: 1. Permission for push notifications is requested. 2. If a user allows notifications, `onSubscribe` event is triggered. `Pushwoosh.subscribe()` is executed automatically if `autoSubscribe: true`. is set during the SDK initialization. Call this method if you have chosen to manually prompt a user to subscribe for pushes using the `autoSubscribe: false` parameter during the initialization: ```html ``` **Pushwoosh.unsubscribe()** 1. `/unregisterDevice` method is executed. 2. `onUnsubscribe` event is triggered. ```html ``` **Pushwoosh.isSubscribed()** Checks if a user is subscribed and returns true/false flag. ```javascript Pushwoosh.isSubscribed().then((isSubscribed) => { console.log('isSubscribed', isSubscribed); }); ``` **Pushwoosh.getHWID()** Returns Pushwoosh HWID. ```javascript Pushwoosh.getHWID().then((hwid) => { console.log('hwid:', hwid); }); ``` **Pushwoosh.getPushToken()** Returns push token if it is available. ```javascript Pushwoosh.getPushToken().then((pushToken) => { console.log('pushToken:', pushToken); }); ``` **Pushwoosh.getUserId()** Returns [**User ID**](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/users-userids/) if available. ```javascript Pushwoosh.getUserId().then((userId) => { console.log('userId:', userId); }); ``` **Pushwoosh.getParams()** Returns a list of the following parameters: ```javascript Pushwoosh.getParams().then((params) => { params = params || {}; var hwid = params.hwid; var pushToken = params.pushToken; var userId = params.userId; }); ``` **Pushwoosh.isAvailableNotifications()** Checks if a browser supports the Pushwoosh WebSDK 3.0, returns ‘true’ or ‘false’. ```plaintext Pushwoosh.isAvailableNotifications() // true/false ``` ### InboxMessages methods **messagesWithNoActionPerformedCount(): Promise** Returns the number of opened messages. ```javascript Pushwoosh.pwinbox.messagesWithNoActionPerformedCount() .then((count) => { console.log(`${count} messages opened`); }); ``` **unreadMessagesCount()** Returns the number of unread messages. ```javascript Pushwoosh.pwinbox.unreadMessagesCount() .then((count) => { console.log(`${count} messages unread`); }); ``` **messagesCount(): Promise** Returns the total number of messages. ```javascript Pushwoosh.pwinbox.messagesCount() .then((count) => { console.log(`${count} messages`); }); ``` **loadMessages(): Promise** Loads the list of undeleted messages. ```javascript Pushwoosh.pwinbox.loadMessages() .then(() => { console.log('Messages have been loaded'); }); ``` **readMessagesWithCodes(codes: Array): Promise** Marks messages as read by Inbox_Ids. ```javascript Pushwoosh.pwinbox.readMessagesWithCodes(codes) .then(() => { console.log('Messages have been read'); }); ``` **performActionForMessageWithCode(code: string): Promise** Performs the action assigned to a message and marks the message as read. ```javascript Pushwoosh.pwinbox.performActionForMessageWithCode(code) .then(() => { console.log('Action has been performed'); }); ``` **deleteMessagesWithCodes(codes: Array): Promise** Marks messages as deleted. ```javascript Pushwoosh.pwinbox.deleteMessagesWithCodes([code]) .then(() => { console.log('Messages have been deleted'); }); ``` **syncMessages(): Promise** Synchronizes messages with the server. ```javascript Pushwoosh.pwinbox.syncMessages() .then(() => { console.log('Messages have been synchronized'); }); ``` ### WebPopups methods The programmatic [Web popups](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#web-popups) API is available whenever the widget is loaded, which is the default (see above). Access it via `Pushwoosh.moduleRegistry.webPopups` once the `web-popups-ready` event has fired. **show(code: string): Promise** Shows a popup immediately, bypassing every display condition (delay, page rules, device/visitor type, frequency capping, trigger type). For a Popup campaign, closes an already visible popup to make room. An Inline campaign renders into its page element instead, leaves a visible popup open, and doesn’t keep other popups from showing. Resolves `false` instead of rejecting when the popup can’t be shown. For an Inline campaign and a web form, see [Inline campaigns and web forms](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#inline-campaigns-and-web-forms). ```javascript Pushwoosh.moduleRegistry.webPopups.show('popup_code') .then((shown) => console.log('Popup shown:', shown)); ``` **hide(code?: string): boolean** Hides the visible popup. If `code` is passed, only hides it when it matches the visible popup. Returns `false` if nothing was hidden. ```javascript Pushwoosh.moduleRegistry.webPopups.hide(); ``` **hideAll(): boolean** Hides the visible popup and dismisses everything still pending for the current page load. `show()` keeps working afterwards. ```javascript Pushwoosh.moduleRegistry.webPopups.hideAll(); ``` **isVisible(code?: string): boolean** Checks whether a popup is currently visible. Without `code`, checks whether any popup is visible. ```javascript Pushwoosh.moduleRegistry.webPopups.isVisible('popup_code'); ``` **getVisibleCode(): string | null** Returns the code of the popup currently on screen, or `null` if none is visible. ```javascript Pushwoosh.moduleRegistry.webPopups.getVisibleCode(); ``` **getAvailableCodes(): Array** Returns the codes of every popup the server returned for this device. ```javascript Pushwoosh.moduleRegistry.webPopups.getAvailableCodes(); ``` **getState(): object** Returns a snapshot of the Web popup’s state: `visible` (code on screen, or `null`), `queued` (codes waiting for the display slot), `waiting` (codes with an armed delay timer), and `available` (every code the server returned). ```javascript Pushwoosh.moduleRegistry.webPopups.getState(); ``` ## Progressive Web App support To integrate Pushwoosh into your Progressive Web Application (PWA), follow the steps described below. For iPhone and iPad users, also see [Web push on iPhone and iPad](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/ios-web-push/). **1**. Copy the path to your Service Worker file: ```javascript if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/service-worker.js') // <- your service worker url }); } ``` Then, use the serviceWorkerUrl parameter while initializing the WebSDK as follows: ```javascript var Pushwoosh = Pushwoosh || []; Pushwoosh.push(['init', { logLevel: 'error', applicationCode: 'XXXXX-XXXXX', safariWebsitePushID: 'web.com.example.domain', defaultNotificationTitle: 'Pushwoosh', defaultNotificationImage: 'https://yoursite.com/img/logo-medium.png', serviceWorkerUrl: '/service-worker.js', // <- your service worker url }]); ``` WebSDK does not register the new Service Worker immediately; a Service Worker is registered when it’s needed: * when a device receives a push token (on device registration or re-subscription), * when a push token is deleted (on removing a device from user base). It speeds your pages loading by shortening the number of server requests. Browsers do not allow two different Service Workers to be registered at the same time (read more: ), so to make your PWA work correctly, a common Service Worker should be registered for your codebase and the Pushwoosh codebase. **2**. Add the following string to your Service Worker (at the beginning or at the end of, it doesn’t matter): ```javascript importScripts('https://cdn.pushwoosh.com/webpush/v3/pushwoosh-service-worker.js' + self.location.search); ``` Thus you enable receiving and processing of push notifications sent via Pushwoosh services for your Service Worker. Note Pushwoosh won’t affect your codebase. You can always check out our Service Worker at . ## Installing from Google Tag Manager Note Make sure to follow this guide’s [steps 1 to 4](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#integration) before adding the script to Google Manager Tag! Use the following code in your **Google Tag Manager** to initialize Pushwoosh SDK. Create a Custom HTML Tag and paste the code below. Make sure to change your Pushwoosh Application Code, Safari Website ID, and default notification image URL.\ Also set high **Tag Firing** priority (ex: 100) and trigger it on **All Pages**. See below for a screenshot.Copy ```html ``` --- # Web push via Google Tag Manager **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-via-google-tag-manager/ How to implement push notifications on your website through Google Tag Manager --- Implement push notifications in four steps without any coding!\ Pushwoosh WebSDK Lite can be added to your website through Google Tag Manager, making the integration process as easy as possible. Note #### Partial Functionality Note that of all visitors of your website, only those using Chrome or Firefox would be able to subscribe for and receive push notifications. The WebSDK Lite also has limited access to Pushwoosh features, such as custom Tags, UserIDs, or event-triggered messaging. For details, please reach out to our [Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/)! ## Integration ### Firebase Credentials In the [Firebase Console](https://console.firebase.google.com/project/_/settings/cloudmessaging/), choose your project to obtain your Firebase **Server Key** and **Sender ID**. Please see the screenshot below for reference: ![Cloud Messaging tab in Firebase project settings with the Server key and Sender ID](https://docs.pushwoosh.com/web-push-notifications-web-push-via-google-tag-manager-1.webp) ### Pushwoosh Control Panel Go to Pushwoosh [**Control Panel**](https://go.pushwoosh.com/v2/applications) and add a new app. On the opened page, configure Chrome or Firefox platform by clicking the **Configure** button in the respective row. Fill in the form: * The **Server Key** from Firebase Console into the **API key** field; * The **Sender ID** from Firebase Console into the **FCM Sender ID** field; * The exact address of your website into **Website Name** field. ![Send Push page in the classic Control Panel with an arrow to the application code under the project name](https://docs.pushwoosh.com/web-push-notifications-web-push-via-google-tag-manager-2.webp) Make sure to copy the Application Code of the newly created app, you’ll need it later. The app code is highlighted below: ### Google Tag Manager **1.** In Google Tag Manager, choose a container or create a new one. **2.** Create a new Tag and choose **Custom HTML** in Tag Configuration. **3.** Copy the script below and paste it into the HTML field; ```txt ``` **4.** Change number signs `#####-#####` (highlighted on the picture below) in the script to your Pushwoosh Application Code, which you acquired in Step 2. **5.** Scroll down to Triggering options and choose **All Pages**. **6.** Click “Save”. Your Tag is ready! ### Push subscription button Let users subscribe for push notifications with a push subscription button. To set up the button, simply add the following script via Google Tag Manager. ```txt
``` All done! Your website’s visitors can now subscribe for and receive push messages. --- # Windows integration guide **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows/ Discover how to integrate Pushwoosh SDK into your Windows applications --- Welcome to the Windows integration guide for implementing Pushwoosh SDK in your project. This section provides detailed instructions and resources to help you integrate Pushwoosh SDK into your Windows applications, whether you’re using the Windows SDK or the Windows JavaScript SDK. ### Windows SDK integration Learn how to integrate Pushwoosh SDK into your Windows application. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows/windows-sdk-integration/) ### Windows JavaScript SDK Step-by-step instructions for integrating Pushwoosh SDK into your Windows JavaScript applications. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows/windows-javascript-sdk/) --- # Windows Javascript SDK **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows/windows-javascript-sdk/ How to integrate Pushwoosh SDK into your Windows JS project --- [**Download SDK**](https://github.com/Pushwoosh/pushwoosh-windows-sdk)\ [**W8 JS Sample**](https://github.com/Pushwoosh/pushwoosh-windows-sdk/tree/master/Samples/Win8.1JS) Note #### Supports Windows 8.1, Windows 10 ## SDK Integration Note You can use an emulator while working with push notifications. To integrate Pushwoosh into your Windows JS application, follow the steps below. **1.** Download our SDK package. **2.** Add reference to **PushSDK/Project-Win8.1-WNS/PushSDK.winmd** to your project\ Or **PushSDK/Project-Win10-WNS/PushSDK.winmd** if you develop for Windows 10 **3.** Initialize push service with the following code: ```javascript var service = new PushSDK.NotificationService.getCurrent("YOUR_PUSHWOOSH_APP_ID"); service.ononpushaccepted = function (args) { //code to handle push notification //display push notification payload for test only var md = new Windows.UI.Popups.MessageDialog(args.toString()); md.showAsync() } service.ononpushtokenreceived = function (pushToken) { //code to handle push token } service.ononpushtokenfailed = function (error) { //code to handle push subscription failure } service.subscribeToPushService(); ``` **4.** In your **app.onactivated** function add the following: ```javascript app.onactivated = function (args) { if (args.detail.kind === activation.ActivationKind.launch) { if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) { // TODO: This application has been newly launched. Initialize // your application here. //Handle start push PushSDK.NotificationService.handleStartPush(args.detail.arguments); ``` Important If you plan to use Toast notifications, please make sure Toast notifications are swithced on in your application project. Go to Application UI tab in manifest settings, and set **Toast Capable** to **Yes**. Otherwise, Toast notifications will not work on the device. ## Advanced features ### Tags With tags, you can create a set of devices based on different criteria. **1.** Create a list of tags: ```javascript var keys = ["name1", "name2"]; var values = ["value1", "value2"]; ``` **2.** Send tags to Pushwoosh: ```javascript service.sendTag(keys, values); ``` --- # Windows SDK integration **URL:** https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows/windows-sdk-integration/ How to integrate Pushwoosh SDK into your Windows project --- [**Download SDK**](https://github.com/Pushwoosh/pushwoosh-windows-sdk)\ [**Download Sample**](https://github.com/Pushwoosh/pushwoosh-windows-sdk/tree/master/Samples/UniversalWin8.1)\ [**SDK API Docs**](https://github.com/Pushwoosh/pushwoosh-windows-sdk/tree/master/Documentation-WNS) Note #### Supports Windows 8.1 and Windows 10 ## SDK Integration Note You can use an emulator while working with push notifications. To integrate Pushwoosh into your application, follow the steps below. **1.** Download our SDK package. **2.** Add reference to **PushSDK/Project-Win8.1-WNS/PushSDK.winmd** to your project\ Or **PushSDK/Project-Win10-WNS/PushSDK.winmd** if you develop for Windows 10 **3.** In the code add PushSDK namespace: `using PushSDK;` **4.** Initialize push service with the following code: ```csharp NotificationService service = PushSDK.NotificationService.GetCurrent("YOUR_PUSHWOOSH_APP_ID"); service.OnPushAccepted += (sender, pushNotification) => { //code to handle push notification string pushString = pushNotification.ToString(); //will return json push payload }; service.OnPushTokenReceived += (sender, pushToken) => { //code to handle push token }; service.OnPushTokenFailed += (sender, errorMessage) => { //code to handle push subscription failure }; service.SubscribeToPushService(); ``` **5.** In your **App.xaml.cs** in **OnLaunched** function add: ```csharp protected override void OnLaunched(LaunchActivatedEventArgs args) { NotificationService.HandleStartPush(args.Arguments); //other code } ``` Important If you plan to use Toast notifications, make sure Toast notifications are switched on in your application project. Go to the Application UI tab in manifest settings, and set **Toast Capable** to **Yes**. Otherwise, Toast notifications will not work on the device. ### Known issues for Windows 8.1 When integrating the Pushwoosh SDK into your Windows 8.1, you might face the following error: ```plaintext The underlying connection was closed: An unexpected error occurred on a send ``` If you encounter that error, add the following code to your project: ```plaintext // Default is 'false' NotificationService.IsWin81(true); ``` --- # Getting started with account management and security **URL:** https://docs.pushwoosh.com/product/account-management-and-security/ Learn to manage your Pushwoosh account, configure security, enable 2FA, handle billing, and organize projects efficiently. --- The Account Management & Security section provides documentation on managing your Pushwoosh account, securing access, and handling billing. It includes guides on the following topics: [Account settings ](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/) [Manage your login and security settings.](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/) [Managing projects ](https://docs.pushwoosh.com/product/account-management-and-security/managing-projects/) [Learn how to edit, and delete projects in Pushwoosh.](https://docs.pushwoosh.com/product/account-management-and-security/managing-projects/) [Managing user access and permissions ](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/) [Invite users and assign permissions based on their roles.](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/) [Two-factor authentication ](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/) [Enhance security by enabling 2FA for your account.](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/) [Passkeys ](https://docs.pushwoosh.com/product/account-management-and-security/passkeys/) [Sign in with device-based passkeys for stronger and faster authentication.](https://docs.pushwoosh.com/product/account-management-and-security/passkeys/) [Billing page ](https://docs.pushwoosh.com/product/account-management-and-security/billing-page/) [Manage your subscription, payments, and invoices.](https://docs.pushwoosh.com/product/account-management-and-security/billing-page/) [Data requests ](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) [Bulk-erase personal data by CSV and review erasure history.](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) --- # Account settings **URL:** https://docs.pushwoosh.com/product/account-management-and-security/account-settings/ Manage Pushwoosh account settings. Update email, password, enable 2FA, set time zone, notifications, view workspace usage metrics. --- The **Account settings** section allows you to manage your login and security settings, time zone, and notification settings, and get an overview of your Pushwoosh usage metrics. To access it, click **My Account** — the profile icon in the top-right corner on the **Projects** page, or the **My account** link at the bottom of the sidebar when you’re inside a project. Then click your account row. ![My account sidebar link and the resulting profile menu, with the account row that leads to Account settings both highlighted](https://docs.pushwoosh.com/shared-61.webp) Below is a breakdown of each subsection and its functionalities. ## Account settings ### Login and security #### Email login The email address you registered with is displayed here. To update it, click **CHANGE EMAIL**. ![Change email option in Account settings](https://docs.pushwoosh.com/account-management-and-security-account-settings-1.webp) In the pop-up window, enter the new email address you wish to use and your current password to confirm the change. Then click the **Get link** button. ![Enter new email and current password to request verification link](https://docs.pushwoosh.com/account-management-and-security-account-settings-2.webp) We will then send a verification link to your current email address to confirm the change. #### Password To update your password, click the **CHANGE** link. In the new window, type your current password and your new password, then click **Change**. Please note that for security reasons, all active password reset emails and user sessions (except this one) will be revoked after the password change. ![Change password form with current and new password fields](https://docs.pushwoosh.com/account-management-and-security-account-settings-3.webp) If you wish to sign out of your account, you can do so by clicking on the **Logout** button in this section. ### Two-factor authentication You can turn on two-factor authentication (2FA) to add another layer of security to your account. Pushwoosh provides two options for two-factor authentication: email verification and an authentication app (e.g., Google Authenticator). You have the flexibility to enable both email verification and the authentication app. This allows you to choose either method when logging in, and ensures you have a backup option in case there are issues with receiving codes. To set up two-factor authentication, select the authentication method in the **Two-Factor Authentication** section. ![Select two-factor authentication method: email or authentication app](https://docs.pushwoosh.com/shared-40.webp) #### Email verification Click **Set up** to receive a verification code. Enter the code sent to your email in the provided field to activate two-factor authentication. ![Enter email verification code to enable two-factor authentication](https://docs.pushwoosh.com/account-management-and-security-account-settings-4.webp) If two-factor authentication is enabled on your account, you will see a **Your account is protected** message along with a green **Email Confirmed** badge. To log in to Pushwoosh, you will need to enter a verification code that will be sent to your registered email. ![Your account is protected with Email Confirmed badge](https://docs.pushwoosh.com/shared-2.webp) To turn off two-factor authentication, click the **Deactivate** button and enter the verification code sent to your email in the pop-up window that appears. #### Authentication app To quickly and securely generate verification codes, use an app like Google Authenticator. Click **Set up** next to the **Authentication app** to configure this option. ![Set up the authentication app for two-factor authentication](https://docs.pushwoosh.com/shared-35.webp) Open your authentication app and scan the QR code that appears. Then enter the verification code provided by the app. ![Scan the QR code in your authenticator app and enter the code](https://docs.pushwoosh.com/shared-23.webp) Once the setup process is complete, you will see the **Confirmed** status next to the **Authentication app** option. ![Confirmed status shown for the authentication app](https://docs.pushwoosh.com/shared-19.webp) ### Default timezone A new account takes the time zone your browser reports at sign-up, whether you sign up with the regular form, an invite, or Google, falling back to Coordinated Universal Time (UTC) if the browser doesn’t report one. To change your preferred time zone, click **CHANGE**. This opens a pop-up window where you can select your desired default time zone. Once selected, click the **Change** button to confirm. Your selected time zone will be used for scheduling campaigns, sending scheduled messages, and viewing statistics. ![Default time zone selection pop-up](https://docs.pushwoosh.com/account-management-and-security-account-settings-5.webp) Note If your browser’s time zone no longer matches your account’s time zone, [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#filter-by-date-range) and the scheduling step of a one-time message show a banner offering to switch your account to the browser’s time zone. ### Interface language Switch the interface language from **My Account**. Click your name at the bottom of the sidebar, or the profile icon in the top bar, then open **Language** and pick one of 14 languages. Both menus show the same list and apply the change immediately, reloading the page. ![Language submenu open in the My Account menu, showing the list of interface languages](https://docs.pushwoosh.com/account-management-and-security-account-settings-12.webp) The **Account settings** page no longer has a separate language control. ### Interface theme Change the appearance of the Control Panel from the same **My Account** menu. Click your name at the bottom of the sidebar, or the profile icon in the top bar, open **Theme**, and select **Light**, **Dark**, or **System**. **System** follows your operating system’s theme setting. ![Theme submenu open in the My Account menu from the top bar](https://docs.pushwoosh.com/account-management-and-security-account-settings-13.webp) You can also change it here, on the **Account settings** page. Select **Light**, **Dark**, or **Auto** from the drop-down below. **Auto** is the same setting as **System** in the **My Account** menu. ![Interface theme selector set to Light in Account settings](https://docs.pushwoosh.com/account-management-and-security-account-settings-11.webp) This setting is stored per browser, so it does not carry over if you log in from a different browser or device. ### Notification settings Turn on the toggle to subscribe to our newsletter and receive updates about Pushwoosh. ![Newsletter subscription toggle in Notification settings](https://docs.pushwoosh.com/account-management-and-security-account-settings-6.webp) ## Workspace This section provides an overview of your current workspace activity. **Only users with administrator rights can view and manage this section.** ![Workspace overview section with activity metrics](https://docs.pushwoosh.com/account-management-and-security-account-settings-7.webp) The name of your current workspace is displayed at the top of the section. To change it, click the **CHANGE NAME** link. In the pop-up window, enter a new name and click Change to confirm the change. ![Change workspace name pop-up](https://docs.pushwoosh.com/account-management-and-security-account-settings-8.webp) Note that this won’t affect any login or billing settings and will only be visible to you and your team. ### Usage metrics In this section, you will also find a detailed summary of your Pushwoosh usage statistics, which include: | Total subscribers | The number of subscribers out of a maximum allowed. | | ------------------------- | --------------------------------------------------------------------------------------- | | Last MAU | The number of Monthly Active Users (MAU) from the previous month | | Current MAU | The number of Monthly Active Users for the current month | | Total applications | The number of applications registered with the workspace out of a total allowed number. | | Emails sent last month | The number of total emails sent from the workspace in the last month | | Emails sent current month | The number of emails sent in the current month | ### Switching between workspaces If you manage multiple workspaces, you can easily switch between them using the **Switch workspace** button. --- # Billing page **URL:** https://docs.pushwoosh.com/product/account-management-and-security/billing-page/ View plan charges, invoice history, and payment methods on the Billing page, and manage subscription updates in your account. --- The Billing page is where you view your Pushwoosh plan charges, invoice history, and saved payment methods, and manage your subscription. To access your financial information and payment methods, navigate to **My account** > **Billing**. In the same menu, click **Upgrade plan** to change your subscription. To compare plans and pricing, see the [Pushwoosh pricing page](https://www.pushwoosh.com/pricing/). ![My account menu with Billing selected and Upgrade plan option visible for opening billing and subscription settings.](https://docs.pushwoosh.com/account-management-and-security-billing-page-1.webp) The page is organized into these sections: * **Overview:** Your plan, estimated charges, billing contact, and usage. * **Invoices:** Billing history with PDF downloads. * **Payment methods:** Saved cards and the default payment method. Note The available sections may vary depending on your plan. The **Billing info** and **Add payment method** buttons at the top of the page work from any tab. On the free plan, an **Activate subscription** button appears there too. ## Overview In **Overview**, you can review your plan charges, billing details, usage, and ManyMoney AI daily request usage. * **Plan:** shows your plan name, billing cycle, and next payment date. You can also see how charges are calculated and the estimated total for the current period. On the free plan, click **Upgrade plan** in this card or **Activate subscription** at the top of the page to choose a paid plan. * **Payment information:** shows who receives invoices and which card is charged by default. * **Usage:** shows how much of your plan you have used in the current billing period, including [monthly active users](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#monthly-active-users-mau) and email sends. * **ManyMoney AI:** shows your daily request usage and when the limit resets. ![Billing page Overview tab showing plan summary, payment information, usage metrics, and ManyMoney AI request status.](https://docs.pushwoosh.com/account-management-and-security-billing-page-2.webp) ### How to manage your billing information If billing is not yet configured, click **Add billing info** in the **Payment information** section. To update existing billing information, click **Billing info** at the top of the page or **Manage billing info** in the **Payment information** section. In the billing information form, enter your billing company name, primary and secondary contact details, and postal address, then click **Save**. ![Billing info form with billing company name, primary and secondary contacts, postal address, and the Save button.](https://docs.pushwoosh.com/account-management-and-security-billing-page-3.webp) ## Invoices In the **Invoices** tab, you can review your billing history and download invoice files for each billed period. Each row shows the invoice creation date, plan name, billing period, charged amount, payment status, and a **PDF** link to open or download the invoice. ![Invoices tab on the Billing page with creation date, plan details, invoice total, PDF link, and paid status.](https://docs.pushwoosh.com/account-management-and-security-billing-page-4.webp) ## Payment methods Your saved cards and PayPal accounts are listed in the **Payment methods** tab. To add a method, click **Add payment method** at the top of the page and select **Debit/Credit Card** or **PayPal**. For PayPal, complete the authorization in the window that opens. For a card, fill in the card number, expiration date, cardholder name, and CVV/CVC, then click **Save**. ![Add debit or credit card form with fields for card number, expiration date, cardholder name, and CVV CVC.](https://docs.pushwoosh.com/account-management-and-security-billing-page-5.webp) After you add a method, it appears in the payment methods list. ![Billing Payment methods tab with the PayPal authorization window opened after selecting PayPal.](https://docs.pushwoosh.com/account-management-and-security-billing-page-12.webp) To delete a method, click the three-dot menu next to it and select **Remove method**. --- # Data requests **URL:** https://docs.pushwoosh.com/product/account-management-and-security/data-requests/ Bulk-erase personal data by CSV from Pushwoosh, and review erasure history with per-identifier results for your account. --- Use **Data requests** to permanently erase personal data from Pushwoosh for people you identify by **User ID**, **HWID**, or **Email**, and to review past erasures on this account. To open it, click **My account** in the top bar or sidebar, then select **Data requests**. ![Data requests list of erasure operations with Started, Started by, Identifier, Applications, Status, Result, and Report.](https://docs.pushwoosh.com/account-management-and-security-data-requests-1.webp) Any team member can open the page, but running **Erase from a file** requires modify rights on the selected application. To erase a single subscriber instead of a file, use **Erase personal data** on their card in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#erase-personal-data). Those erasures land in this same table. Caution Erasing a subject removes their data in Pushwoosh, but doesn’t cancel a message already scheduled or queued for them, for example a delayed send or a running journey’s next step. Pushwoosh keeps this journal, including the per-identifier report, for 3 years. ## Erase from a file Click **Erase from a file** to bulk-erase personal data by a list of identifiers. Danger Erasure cannot be undone. Matched personal data is permanently removed from Pushwoosh for the applications you select. The subject list is a CSV with one identifier per row, up to 5 MB and 10,000 identifiers. Select up to 50 applications, and keep identifiers × applications to 50,000 or fewer, because that’s how many rows the report holds. The first row may name the column. 1. In **Applications**, select which applications to search for the identifiers. 2. In **Identifier type**, choose **User ID**, **HWID**, or **Email**, matching what the CSV column contains. 3. Under **Subject list (CSV)**, click **Choose a file**. 4. Select the CSV to upload. ![Erase personal data from a file dialog with Applications, Identifier type, and Subject list CSV upload fields.](https://docs.pushwoosh.com/account-management-and-security-data-requests-2.webp) 5. Click **Count the volume** to see how many records match, plus duplicate and skipped-row counts from the file, before erasing anything. You can stop after this step without erasing. To see those count-only runs in the table later, turn on **Show volume counts**. 6. Click **Erase N subjects** once the count looks right, where N is the number of matched records. This step cannot be undone. ![Erase personal data from a file dialog after Count the volume, showing a match count and the enabled Erase N subjects button.](https://docs.pushwoosh.com/account-management-and-security-data-requests-3.webp) An account can start up to 20 data erasure runs in a rolling 24 hours, including volume checks. Past that, **Erase from a file** and **Erase personal data** refuse to start another one until enough of those 24 hours have passed. ## Review past erasures The table lists erasure operations on this account. Each row is one run. * **Started by:** check who started the run. It shows the address of the Control Panel user, or **[API token](https://docs.pushwoosh.com/developer/api-reference/data-erasure-api/#getjournalreport)** if the run was started through the API. A dash marks older runs from before this was recorded. * **Status:** for example **Done**. * **Result:** how many identifiers were **Erased** and how many were **not found**. * **Report:** click **CSV** to download the per-identifier outcome for that operation. ### Show volume counts To see runs where you only clicked **Count the volume** and didn’t erase anyone, turn on **Show volume counts**. By default, the table hides them and shows only completed erasures. These runs show **Count only** in the **Identifier** column, under the identifier type (**HWID**, **User ID**, or **Email**). Caution A **Count only** row is a volume check, not an erasure. No data was removed. Its **Result** still reads **Erased: N**, where N is how many identifiers in your file matched. ![Past erasures table with Show volume counts on, a red box highlighting Count only under HWID next to a completed erasure.](https://docs.pushwoosh.com/account-management-and-security-data-requests-4.webp) To narrow or export the list: 1. In **ANY PERIOD**, choose the period. 2. Choose an application in **All applications**, or leave **All applications** selected. 3. In **Any initiator**, pick a team member from the list or type any address. This also finds someone no longer on the account, because their past erasures stay in the journal under their address. 4. Click **Export period** to download a CSV of the operations shown for the selected period. --- # Manage projects **URL:** https://docs.pushwoosh.com/product/account-management-and-security/managing-projects/ Manage Pushwoosh projects including viewing details, editing settings, permissions, and deleting projects --- A project in Pushwoosh represents a single app you send messages to. This page covers how to view a project’s details, edit its settings and permissions, and delete the project when it’s no longer needed. Note To learn how to **create a mobile, web or email project**, please refer to this [guide in our documentation](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project). ## Project details To see project details, choose the project from the list, and click its name at the top left of the page. ![Project details page with app code and device metrics](https://docs.pushwoosh.com/account-management-and-security-managing-projects-1.webp) Here, the app code can be copied to the clipboard with a single click. * The number of **enabled devices** shows how many devices are reachable with push notifications (i.e., they have push tokens assigned by cloud messaging gateways). * **Total devices** are the devices registered for this app with or without push tokens. They are reachable with In-Apps even if there’s no push token assigned. ![Project dropdown in the sidebar with application details and the Manage projects link](https://docs.pushwoosh.com/account-management-and-security-managing-projects-3.webp) To see the list of all your projects, click **Manage projects**. ## Editing projects To edit project settings or configuration, choose the project, then go to the **Settings** section. ![Settings section in sidebar with Configure Platforms, Message delivery settings, Control groups, API Access, Deep Links, and Test Devices](https://docs.pushwoosh.com/account-management-and-security-managing-projects-4.webp) In the **Settings** section, you can: * [configure or reconfigure project platforms](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) (iOS, Android, Web, Email, etc.). * [manage message delivery settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/) for frequency capping and send rate limits. * [connect 3rd party integrations](https://docs.pushwoosh.com/product/integrations/) with external tools and services. * [set up control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups) to exclude a share of your audience from campaigns for unbiased measurement. * [create API tokens](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#create-a-new-api-access-token) and [manage access permissions](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#manage-a-token). * [create and edit Deep Links](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) that take users directly to specific app pages when they open push messages. * [add Test Devices](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices) to test your messages before sending them to subscribers. * [set permissions for sub-users](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts) to view or manage this particular project. ### Edit project details To modify the project settings, go to the **Configure platforms** page in the **Settings** section and click **Edit project** in the top right corner. ![Edit project dialog showing TITLE, icon with Browse, Bit.ly checkbox and Access token field, and email notification settings with Save and Cancel buttons](https://docs.pushwoosh.com/account-management-and-security-managing-projects-6.webp) Here you can modify the following: * **Title**: Update the project name in the **TITLE** field. * **Icon**: Change the project icon by clicking **Browse…** next to the current icon and selecting a new image file. * **Bit.ly integration**: Enable the **I want to add my bit.ly account** checkbox and enter your bit.ly access token in the **ACCESS TOKEN** field to integrate your Bit.ly account for link shortening. * **Email notifications**: Configure email notification settings. Enable **I want to receive Daily Application Performance Emails** to receive daily performance reports. The email address where notifications will be sent is displayed below the checkbox. ![Project settings page showing available configuration options including platforms, API tokens, Deep Links, Test Devices, and sub-user permissions](https://docs.pushwoosh.com/account-management-and-security-managing-projects-7.webp) Click **Save** to apply your changes or **Cancel** to discard them. ## Deleting projects To delete a project, go to the **Settings** section and navigate to the **Configure platforms** page. Open the three-dot menu at the top right of the page (next to **Edit project**) and select **Delete project**. Caution Deleting a project is a permanent action. The project will be removed from your account, and all the project’s user base will be removed as well. This action cannot be undone. ![Configure platforms page with three-dot menu at top right opening Delete project option](https://docs.pushwoosh.com/account-management-and-security-managing-projects-5.webp) --- # Manage user access and permissions **URL:** https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/ Manage user access and roles in Pushwoosh. Invite team members, assign permissions, and control who can access your account. --- Pushwoosh enables your entire team to work together within a single account, allowing team members to perform their specific tasks simultaneously. You can invite team members and assign permissions based on their roles and responsibilities. This ensures efficient collaboration while maintaining control over account access. To manage team access, assign roles, and invite new users, go to **My Account → [Users and groups](https://app.pushwoosh.com/accounts/users-and-roles)**, which opens the **Users and roles** page (the menu item and the page title name the same screen). Opening it requires the **Manage users** permission. ![Pushwoosh My Account menu with Users and groups highlighted](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts.webp) Team access is split across four tabs: **Users**, **Invites**, **Roles (ex. groups)**, and **Auth History**. ## Users The **Users** tab displays a list of all team members with access to your account. The account owner’s role can’t be changed and their access can’t be revoked. ![Pushwoosh Users tab listing team members and assigned roles](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-1.webp) In this tab, you can: * Search for users by email. * Filter users by role using the dropdown menu. * View assigned roles for each user. * Change a user’s role or revoke access by clicking the **three-dot menu** next to their email and selecting the desired option. ### How to invite a user To add new users to your account by sending them an email invitation with a verification link, follow these steps: 1. Click **Invite user**. ![Pushwoosh invite user dialog to add team member](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-2.webp) 2. In the **Role** field, select the appropriate role for the new user. Note If no roles are available, you must **create one first**. [Learn more about roles](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#creating-and-managing-roles-ex-groups) 3. Enter the user’s **email address** in the provided field. ![Pushwoosh send invite form with email field](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-3.webp) 4. Click **Send invite** to send the invitation. Once the invitation is sent, the recipient’s email, invite date, and status will appear in the **Invites** list. 5. The invited user will receive an email with a verification link. After clicking the verification link, the user will need to either sign up or sign in. They can also choose to sign in with Google. Once complete, they’ll automatically gain access to the account they were invited to. ![Pushwoosh sign up screen to accept account invite](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-8.webp) #### Invitation statuses Each invitation has a status indicating its current state: * **Accepted**: The user has accepted the invitation and successfully joined the account. * **Sent**: The invitation has been sent but has not yet been accepted. ![Pushwoosh invitation statuses accepted and sent list](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-3.1.webp) If the invitation has not been accepted or the invitee hasn’t received it, click **Re-send** to send it again after an hour. To revoke access, click the **three-dot menu** next to the invitee’s email address and select **Revoke access**. Tip To secure your account, set up [Two-factor Authentication](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication) for team members. ## Creating and managing roles (ex. groups) The **Users and roles** section allows administrators to manage user accounts, roles, and permissions within the system. ### Create a new role You can create custom roles with specific permissions to manage user access within the system. To create a new role: 1. Click **Add role** in the top-right corner. ![Pushwoosh Add role button in Users and roles](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-4.webp) 2. Enter a role name in the **Name** field. 3. Select the required permissions by checking the corresponding boxes. ![Pushwoosh role permissions selection checkboxes](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-5.webp) #### Available permissions | Category | Permission | Description | | :--------------------- | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Account management** | Manage users | Allows managing users, roles, and permissions. | | | Manage subscription | Allows managing billing information, payment methods, and purchases. | | | API access | Enables viewing, editing, and generating API access tokens. Also requires **Personal data**, because an API token can read the whole audience. | | | Domain and email verification | Allows adding, verifying, and editing domains and email addresses. | | **Applications** | See applications | Allows viewing application-related data. | | | Modify applications | Controls access to managing campaigns, push presets, test devices, configurations, events, and deep links. | | **Messages** | See messages | **Important:** Required for sending and deleting messages. | | | Send messages | Grants permission to send push notifications, emails, autopushes, geozones, and RSS feeds. | | | Approve messages | Lets the user review submitted push presets and email templates and approve or reject them. Only relevant when [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for the account. | | | Delete messages | Allows deleting messages from the system. | | **Ad inventory** | See email ads | Allows opening the [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) screen to view campaigns and creatives, and the campaign picker in [Sponsored push](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign). Only relevant when **Ad slot** is enabled for the account. | | | Modify email ads | Allows creating, editing, and deleting campaigns and creatives in the Ad inventory. Without this, the screen is read-only. | | **Personal data** | Personal data | Allows viewing and exporting the audience: devices, emails, phone numbers, HWIDs, user IDs, and push tokens. Without it, the role can still create and send messages. See [What the Personal data permission controls](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls). | 4. Review the selected permissions. 5. Click **Save** to create the role. To cancel the process, click **Cancel**. The new role will now appear in the **Roles (ex. groups)** list and can be assigned to users as needed. #### What the Personal data permission controls The **Personal data** permission decides whether a role can see who is in the audience. Without it, users of the role still build segments, send messages, and see counts and aggregate statistics, but every screen and export that lists individual users is unavailable: * [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) and the [Email suppression list](https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/). * [Segment exports](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#export-segment-users), the list of users on a segment’s page, and the user list export for an event. * Recipient exports in [push](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#export-recipients), [email](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#export-recipients), and [SMS and messenger](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#export-recipients) statistics, **Find recipient**, and [bounced email and failed device downloads](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/) in the delivery report. * The [**Audience** tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/#audience-tab) and form submission exports in in-app statistics. * User exports in Customer Journey: [**Export users** on an element](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#export-statistics-for-each-step-of-your-customer-journey), [**Export point users**](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#message-level-statistics), and the [user flow search](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/) on the canvas. * [Subscriber exports](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/#export-subscribers) for subscription forms and [user exports for control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#export-control-group-users). * [API access tokens](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#create-and-manage-api-access-tokens), because a token can export the same data. New roles don’t have the **Personal data** permission by default. The default **Admins** role has it. To grant it to another role, select **Personal data** when you [edit role permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#edit-role-permissions). If your role doesn’t have the **Personal data** permission, the buttons and menu items listed above aren’t shown to you at all. That’s why a colleague with another role may see an export button that you don’t. User Explorer and the Email suppression list show an **Access forbidden** page instead. ### Manage roles The **Roles (ex. groups)** tab allows administrators to view, manage, and configure user roles within the system. It displays a list of existing roles along with the number of users assigned to each role. You can use the search bar to quickly filter roles by name. To manage a specific role, click the **three-dot menu (⋮)** next to it to access available actions such as editing permissions, managing members, or deleting the role. #### Edit role permissions 1. Click the **three-dot menu** (⋮) next to the role. 2. Select **Edit permissions**. 3. Modify the permissions as needed. 4. Save the changes to apply the updated permissions. #### Manage role members 1. Click the **three-dot menu** (⋮) next to the role. 2. Select **Manage members**. 3. In the pop-up window that appears, select or deselect users to assign or remove them from the role. ![Pushwoosh manage role members modal](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-6.webp) 4. Click **Apply** to save the changes. #### Delete a role To delete a role: 1. Click the **three-dot menu** (⋮) next to the role you want to remove. 2. Select **Delete role**. 3. If users are assigned to the role, select an alternative role from the dropdown menu to reassign them. 4. Click **Delete** to confirm and remove the role. Caution Deleting a role is permanent and can’t be undone. The role and its permission configuration are removed from the account. Only the users who were on it move to the role you picked in step 3. ## View authentication history The **Auth History** tab displays a log of recent authentication activity for all users in your account. This feature enables administrators to monitor access, review successful or failed login attempts, and investigate any unusual activity. The log includes the following details: * **User email**: The email address used during the login attempt. * **Status**: Indicates whether the authentication attempt was successful or failed. * **IP address**: The IP address from which the login attempt originated, useful for identifying unexpected access locations. * **Timestamp**: The exact date and time of the login attempt. Entries are listed in chronological order. You can also use the search function to filter the authentication history by user email, allowing for quick access to specific records. ![Pushwoosh Auth History tab listing login attempts by email, status, IP, and date](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-15.webp) ## App-specific permissions If you want to grant users access to specific apps instead of the entire account, use **App-specific permissions**. These permissions are configured in the **Application Permissions** section of each individual app in your account. App-specific permissions provide the same level of access as the equivalent **All applications permissions**, but they apply only to the selected app. ![Pushwoosh app-specific permissions settings](https://docs.pushwoosh.com/account-management-and-security-multi-login-accounts-7.webp) > **Note:** Even if the **See Applications** permission is not enabled for a user group, you can still assign app-specific permissions to give them access to selected apps. --- # Set up passkeys **URL:** https://docs.pushwoosh.com/product/account-management-and-security/passkeys/ Learn how to set up passkeys in Pushwoosh, choose a storage option, manage passkeys, and sign in securely without a password. --- [**Passkeys**](https://www.passkeycentral.org/introduction-to-passkeys/) let you sign in to Pushwoosh without a password. Instead of typing a password or waiting for an SMS code, you confirm access with Touch ID, Face ID, Windows Hello, or a device PIN. ## What are the benefits of passkeys? * **Phishing-resistant sign-in:** passkeys work only on the correct domain, so they cannot be used on a fake login page. * **No shared secret on the server:** Pushwoosh stores only a public key, which cannot be used to sign in on its own. * **Faster login flow:** for passkey sign-ins, confirm access with Touch ID, Face ID, Windows Hello, or a device PIN instead of entering a password and SMS code. * **Better protection than SMS codes:** passkeys are not tied to your phone number, which reduces SIM-swap risks. ## Enable passkeys in Pushwoosh Note You can add multiple passkeys to your Pushwoosh account, for example one for each device you use. Each passkey is independent and can be deleted separately. To add a passkey: 1. Go to **My Account** > **Account settings**. 2. In the **Passkeys** section, click **Add a passkey**. ![Pushwoosh Account settings page showing the Passkeys section and the Add a passkey button.](https://docs.pushwoosh.com/account-management-and-security-passkeys-1.webp) 3. Enter a name for the passkey, then click **OK**. ![Pushwoosh passkey setup — naming a new passkey in the browser prompt before saving.](https://docs.pushwoosh.com/account-management-and-security-passkeys-2.webp) 4. Select where to save the passkey. ### Passkey storage options When you save a passkey, you can choose from several storage options. * **Sign in again (Google Account):** saves and syncs the passkey across devices linked to your Google Account. Availability on other devices depends on your Google Account and Chrome sync settings. Typically used when you sign in with Chrome and use Google Password Manager. * **iCloud Keychain:** saves and syncs the passkey across your Apple devices, including Mac, iPhone, and iPad. Typically used when your main devices are in the Apple ecosystem. * **Use a phone or tablet:** lets you scan a QR code and use a nearby mobile device as the passkey provider. Use this when you need to sign in from a desktop without saving a passkey on that desktop. * **Your Chrome profile:** saves the passkey in the current Chrome profile on this device. Availability on other devices depends on your Chrome and Google Password Manager sync settings. * **USB security key:** saves the passkey on a physical FIDO security key such as YubiKey. Typically used in high-security environments or where hardware-based authentication is required. Passkey availability depends on your browser, operating system, and selected passkey provider settings. ![Pushwoosh passkey setup — system passkey chooser listing storage options including iCloud Keychain and Google Account.](https://docs.pushwoosh.com/account-management-and-security-passkeys-3.webp) 5. Complete the confirmation step shown for your selected storage option. For example, if you choose **iCloud Keychain**, confirm with Touch ID. 6. Return to **Account settings** and verify that the new passkey appears in the **Passkeys** list with a **Synced** status. ![Pushwoosh Account settings showing a newly added synced passkey with a Delete button.](https://docs.pushwoosh.com/account-management-and-security-passkeys-4.webp) If the passkey shows an error status, the save step did not complete successfully. Click **Add a passkey** and try again. Tip Set up at least one backup sign-in method in **My Account** > **Account settings** > **Two-Factor Authentication**: * **[Email verification](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/#email-verification):** sends a code to your confirmed email address. * **[Authentication app](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/#authentication-app):** generates codes in an app such as Google Authenticator. If your primary device is unavailable, you can still sign in using one of these methods. If you lose access to all sign-in methods, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/). ## Delete a passkey 1. Go to **My Account** > **Account settings**. 2. In the **Passkeys** section, find the passkey you want to remove. 3. Click **Delete** next to it. ![Pushwoosh Account settings page showing the Passkeys list with the Delete action for a saved passkey.](https://docs.pushwoosh.com/account-management-and-security-passkeys-5.webp) Note Deleting a passkey in Pushwoosh removes it from your account. If the passkey was saved to iCloud Keychain or Google Password Manager, it may still appear as a sign-in option in your browser. To avoid confusion, also delete it from your device password manager. ## Sign in with a passkey When signing in to Pushwoosh, a passkey can be used in two ways: * If a passkey is available, your browser may suggest it automatically when you enter your username or email. * If no suggestion appears, enter your username or email, click **Sign in**, and then click **Sign In With Passkey**. Confirm sign-in on your device with biometrics or a device PIN. --- # Set up two-factor authentication **URL:** https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/ Enable two-factor authentication in Pushwoosh to protect your account with email verification codes or an authenticator app --- You can add an extra layer of security to your account with two-factor authentication (2FA). Pushwoosh provides two options for two-factor authentication: email verification and an authentication app (e.g., Google Authenticator). You have the flexibility to enable both email verification and the authentication app. This allows you to choose either method when logging in, and ensures you have a backup option in case there are issues with receiving codes. Email verification is on by default for the account owner When you create a Pushwoosh account, email verification is turned on automatically for the account owner. You don’t need to go through the setup steps below to get the **Email Confirmed** badge. Invited users enable two-factor authentication for their own accounts themselves, as described in [Setting up two-factor authentication for sub-accounts](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/#setting-up-two-factor-authentication-for-sub-accounts). ## Enabling two-factor authentication **1.** Log in to your account in **Pushwoosh Control Panel** and click **My Account** — the profile icon in the top-right corner on the **Projects** page, or the **My account** link at the bottom of the sidebar when you’re inside a project. Then click your account row to open **Account settings**. ![My account sidebar link and the resulting profile menu, with the account row that leads to Account settings both highlighted](https://docs.pushwoosh.com/shared-61.webp) **2.** In the **Two-Factor Authentication** section, select the authentication method. ![Choose 2FA method email verification or authentication app](https://docs.pushwoosh.com/shared-40.webp) ### Email verification Click **Set up** to receive a verification code. Enter the code in the provided field to activate two-factor authentication. ![Enter email verification code to enable two-factor authentication](https://docs.pushwoosh.com/account-management-and-security-two-factor-authentication-1.webp) If two-factor authentication is enabled on your account, you will see a ***Your account is protected*** message along with a green **Email Confirmed** badge. To log in to Pushwoosh, you will need to enter a verification code that will be sent to your registered email. ![Your account is protected with Email Confirmed badge](https://docs.pushwoosh.com/shared-2.webp) To turn off two-factor authentication, click the **Deactivate** button and enter the verification code that was sent to your email in the pop-up window that appears. ### Authentication app To quickly and securely generate verification codes, use an app like Google Authenticator. Click **Set up** next to the **Authentication app** to configure this option. ![Set up the authentication app for 2FA](https://docs.pushwoosh.com/shared-35.webp) Open your authentication app and scan the QR code that appears. Then enter the verification code provided by the app. ![Scan QR code and enter app-generated verification code](https://docs.pushwoosh.com/shared-23.webp) How authentication apps work The authentication app generates TOTP (Time-based One-Time Password) codes, a standard defined in RFC 6238. TOTP creates a short-lived numeric code based on time and a shared secret key. After the initial setup, the app generates codes entirely offline; no internet connection is needed to create the codes. Once the setup process is complete, you will see the **Confirmed** status next to the **Authentication app** option. ![Confirmed status shown for the authentication app](https://docs.pushwoosh.com/shared-19.webp) When a verification code isn't requested For the first month after an account is created, logging in doesn’t ask for a verification code, even with two-factor authentication enabled, no matter which method you set up. Starting one month after account creation, logins prompt for the code as usual. When you enter a verification code at login, you can enable **Trust This Device** to skip the code on that device for 30 days. ## Setting up two-factor authentication for sub-accounts [Invited users or sub-users](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#how-to-invite-a-user) can set up two-factor authentication for their accounts using the same process as account owners. Each user must configure their own two-factor authentication settings. To enable two-factor authentication: **1.** Log in to your account in **Pushwoosh Control Panel** and click **My Account** — the profile icon in the top-right corner on the **Projects** page, or the **My account** link at the bottom of the sidebar when you’re inside a project. Then click your account row to open **Account settings**. **2.** Follow the same setup steps described in the [Enabling two-factor authentication](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication/#enabling-two-factor-authentication) section above, choosing either email verification or an authentication app. Once two-factor authentication is enabled, you will need to enter a verification code each time you log in to your account. --- # Getting started with audience data and segmentation **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/ Learn how to collect, manage, and segment user data in Pushwoosh to create highly targeted messaging campaigns. --- Pushwoosh provides powerful tools to help you **understand, categorize, and engage your users effectively**. This section covers how to **collect, manage, and segment user data**. This enables you to create targeted messaging campaigns. ## User data (tags) [Tags ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/)Understand how Tags work in Pushwoosh and how to use them for segmentation. [Update tags via CSV ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/)Bulk update user data efficiently by importing Tags via CSV files. ## Segmentation [Segments ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/)Discover how to define and manage user segments for precise targeting. [How to create segments ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/)Learn how to create segments using tags, events, existing segments, and advanced filtering techniques. [Calculating segment size ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/calculating-segment-size/)Estimate segment size to optimize targeting and campaign performance. [RFM segmentation ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/)Use Recency, Frequency, and Monetary (RFM) segmentation to classify users based on engagement. ## Events [Understanding events in Pushwoosh ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/)Track user actions and behaviors with events to trigger relevant messages. [Default events ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/)Explore predefined events that help track common user actions. [Custom events ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/)Define custom events tailored to your business needs for advanced tracking. [Conversion events ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/)Track purchase revenue in one format with PW_Conversion. Send it from your code or track revenue for existing events in the Control Panel. ### Recommended events for different industries Discover industry-proven in-app events designed for your app category. Based on best practices and expert insights, these events help you track key user interactions. They also refine segmentation and create more impactful messaging campaigns. [Recommended events for e-commerce ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-e-commerce/)Leverage key events to track and optimize user behavior in e-commerce applications. [Recommended events for gaming apps ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/)Monitor user engagement and progression with events designed for gaming apps. [Recommended events for media ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-media/)Analyze content consumption patterns with recommended events for media platforms. [Recommended events for mobile apps ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-mobile-apps/)Track user interactions and engagement metrics in mobile applications. [Recommended events for subscription management ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-subscription-management/)Optimize subscription models using predefined events for user lifecycle tracking. ## Industry starters Set up the data model for a specific industry before building segments, so the tag types match the scenarios you plan to run. [Telecom starter ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/telecom-starter/)Subscriber profile tags, the server-to-server update job, and ready segments for bundle expiry, balance, and roaming. ## Control groups Create a control group that excludes a fixed percentage of users from marketing messages (Push, Email, In-app, SMS, WhatsApp) so you can measure campaign effectiveness by comparing results with users who did not receive the messages. Users in the control group still receive transactional messages. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) ## User Explorer Gain insights into individual user behavior, attributes, and engagement history. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) ## Feature flags Turn app and website features on for chosen segments and a share of users, and switch them off instantly, without releasing a new version. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) ## Collect email subscribers on your website Use subscription forms to capture email signups on your site and send data to Pushwoosh. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) ## Industry starters Set up the data model for a specific industry before building segments, so the tag types match the scenarios you plan to run. [Telecom starter ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/telecom-starter/)Subscriber profile tags, the server-to-server update job, and ready segments for bundle expiry, balance, and roaming. --- # Understanding control groups **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/ Exclude a fixed share of your users from marketing messages to measure campaign impact against the holdout group you set up. --- A control group excludes a fixed share of your users from [marketing messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) on Push, Email, In-App, SMS, and WhatsApp. Pushwoosh compares those users to everyone else in the group’s audience who keeps receiving campaigns, so you can measure whether your messaging drives results. An app can run several control groups at once, for example one per market. Each group holds out its own share of users within its own countries and tag. Control group users still receive [transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) on those channels when you set the message type accordingly. Important * Control groups are configured per application. Each group has its own on/off state, size, scope, and membership. Creating or changing a group in one app doesn’t affect any other app. * Only admins can create, edit, or delete control groups. Roles are assigned in **My Account → Users and groups**. [Learn how to manage roles and permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/). ## How control group membership works Pushwoosh doesn’t keep a stored list of control group members. It calculates membership with a formula on every check: 1. Pushwoosh combines your app, the current group version number, the group, and the user ID. 2. A hash formula turns them into a number. 3. If the number falls within the percentage you set (for example, 5%), the user is in the control group. If not, they aren’t. What this means for you: * **All existing users are covered at once.** Turning the group on applies to your entire user base right away, not only to people who sign up afterward. * **Membership changes only at set moments.** A user stays in or out of the group until one of these happens: you [reshuffle](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group) the group or change its size, the user’s ID changes, or an [Auto-refresh cycle ends or an Experiment reaches its end date](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode) and Pushwoosh selects a new group. * **Re-enabling brings back the same group.** If you disable the group and turn it on again without reshuffling, you get exactly the same users, because the formula hasn’t changed. Link every contact method to one User ID Pushwoosh assigns users by User ID, not by device: * **Contact methods under different User IDs**, such as a mobile device, an email address, or a phone number, count as separate users. One of them can be excluded from marketing messages while another for the same person still receives them. * **Contact methods without a User ID** are never excluded from marketing messages (Push, Email, In-App, SMS, WhatsApp). Pushwoosh needs a User ID to run the membership formula. Register every contact method for the same person under one User ID, so the person has the same control group status on every channel. [Learn how to manage User IDs for omnichannel campaigns](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) ## How several control groups work together Every enabled control group of the app applies to every marketing message the app sends. You don’t pick a group per message. * **Each group selects its own users.** Two groups of the same size hold out different users, because the group is part of the membership formula. * **One group is enough to hold a user out.** If a device falls into the audience of several groups and any of them selects its user, the device doesn’t receive the marketing message. * **Groups can overlap.** Pushwoosh lets you create groups whose countries and tag values match the same users, and warns you about it. Users who match both groups are held out by each of them, so the statistics of both groups are skewed. To keep results clean, give each group its own countries or its own values of the same tag. * **Disabled groups don’t hold anyone out.** They keep their settings and history, and they don’t count toward overlaps. ## How the control group affects message delivery Control group membership blocks or allows messages based on the message type you set for each send. * **Marketing message:** blocked for control group users on Push, Email, In-App, SMS, and WhatsApp. * **Transactional message:** still delivered on those channels. Select **Transactional message** when creating a customer journey or sending a one-time message. * **Silent push:** always delivered, because it carries data for the app, not a message for the user. Choose **Marketing message** or **Transactional message** for every send on those channels. See [where to set the message type](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#where-to-set-the-message-type). Note * **Viber:** control group members never receive Viber messages, even transactional ones. * **Missing message type in [API sends](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#api-requests-and-message-type):** the `message_type` parameter is optional. If you omit it, Pushwoosh treats the send like **Marketing message**, not transactional, so control group users are excluded. Set **Transactional message** in the campaign or `message_type` to transactional in the API to reach everyone, including the control group. [Learn more about marketing and transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Set up and measure your control group ### Create and manage a control group Name the group, set its size and mode, scope it by country or tag, then change, rename, reshuffle, export, disable, or delete it later. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/) ### Analyze control group impact Compare events between users who get your marketing messages and the control group, and read the Impact report. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/) ### Control groups FAQ Answers to common questions about membership, modes, scope, and results. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/control-groups-faq/) ## Related To measure a single campaign or journey against its own holdout instead of the application’s control groups, see the uplift report. [Uplift report ](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/) --- # Analyze control group impact **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/ See how your messaging performs against your control group by comparing events and reading the Impact report for each cycle. --- Each control group’s page shows how your messaging performs against that group. Open it from **Settings > Control groups** by clicking the group’s row. ## Check the group’s mode and dates Below the group’s name, the page shows the **Group size**, the **Mode**, the number of the current **Cycle**, and a date that depends on the [mode](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode): | Mode | Date below the name | Label of the date-range selector | | --------------------------- | --------------------------------------------- | -------------------------------- | | Permanent | **Running since**: the date the group started | No label | | Auto-refresh | **Range** of the current cycle | **Current cycle** | | Experiment with an end date | **Range** of the current cycle | **Current experiment** | To see the results of an earlier cycle, open the date-range selector and pick it under **Previous**. Ended cycles are listed there by number, as **Cycle** or **Experiment**. A Permanent group can have earlier cycles too, because resizing, rescoping, or changing the mode starts a new one. ![Control group page header showing Group size, Mode, and Running since, with a Recalculate link](https://docs.pushwoosh.com/global-control-group-7.webp) ## What the page compares The page has two blocks: an events comparison table at the top and an **Impact report** below it. Both compare two groups of users on any event you choose: * **Treatment group:** users in the group’s audience who are not in the control group. They keep receiving your marketing messages (Push, Email, In-App, SMS, WhatsApp) as usual. * **Control group:** the holdout you configured above. These users are excluded from marketing messages, so any difference in their behavior isn’t influenced by your campaigns. Both groups are drawn from the group’s audience, shown in **Measured among** on the group’s page: **All users**, or the users who match the group’s countries and tag values today. * A user whose tag value changes drops out of the count. * The current cycle’s data updates once a day. When a cycle ends, its final results usually appear under **Previous** within 15 minutes. * Pushwoosh doesn’t filter either group down to users with a valid push token, verified email, or other reachable channel. Treatment and control come from the same set of users, with no separate filtering between them. Results from before August 25–26, 2026 Before August 25–26, 2026, the two groups were counted differently. If your time window includes those dates, **Conv. Control** and **Uplift** may show a sudden step that isn’t caused by anything in your app. ## Compare events 1. Select a time window: **Last 3 days**, **Last 7 days**, or **Last 30 days**. To compare an earlier cycle, pick it under **Previous**. The measurement window restarts, and earlier data is no longer included, when: * you [resize the group or change its countries](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#edit-the-control-group-size-and-countries); * you [reshuffle](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group) the group; * you [change the mode](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode); * an Auto-refresh cycle ends or an Experiment reaches its end date. Data from before that point no longer matches the current members or audience. Ended cycles are listed under **Previous** in the date-range selector. 2. Review the events comparison table. It only shows events automatically if you’ve set [Conversion Goals in Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals). Otherwise, the table starts empty. 3. Click **Add events to compare** to add any event you want to measure. You can add up to 10 events in total. Message-engagement events can't be compared Push or email opens, clicks, sends, and bounces aren’t available here, even if you set one as a Conversion Goal. Control group users never receive marketing messages, so these events would only show that a message was sent, not what it achieved. ![Events comparison table for the current cycle, with Conv. Control, Conv. Treatment, Uplift, and Significance for three events](https://docs.pushwoosh.com/control-groups-events-table.webp) The table shows these metrics for each event: | Metric | What it shows | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Conv. Control** | The share of unique users held out from marketing messages who triggered the event at least once anyway, without any messaging influence. | | **Conv. Treatment** | The share of unique users in the treatment group who triggered the event at least once. The treatment group counts everyone in the group’s audience outside the control group, whether or not a message actually reached them during the window. | | **Uplift** | The percentage difference between the treatment and control conversion rates, relative to the control rate. A positive value means the treatment group converted better. Calculated as `(conv. treatment − conv. control) / conv. control × 100%`. | | **Significance** | Whether the difference is statistically reliable. See the statuses in the next table. | | Significance status | What it means | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Significant** | The uplift is unlikely to be due to chance. Pushwoosh marks it this way when a two-sided z-test of the two conversion rates gives a P-value below 0.05, which is a Confidence above 95%. | | **Not significant** | The difference isn’t statistically reliable yet. | | **Not enough data** | Either group has fewer than 10 conversions or fewer than 10 non-conversions for this event, so significance can’t be calculated. | ## View the Impact report Use the **View event** dropdown to select an event from the table. The **Impact report** below shows a deeper breakdown for that event. ![Impact report showing uplift, conversions driven by messaging, and a group breakdown table](https://docs.pushwoosh.com/global-control-group-8.webp) | Metric | What it shows | | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Uplift (change from control)** | How much the treatment conversion rate differs from the control conversion rate. The same value as in the events table. | | **Conversions driven by messaging** | The estimated number of extra events your messages generated beyond what’s expected without messaging. The percentage shows them as a share of all treatment events. Calculated as `(events per user in treatment − events per user in control) × treatment group size`. | | **Significance** | Whether the difference is statistically reliable: **Significant**, **Not significant**, or **Not enough data**. Based on **Confidence** and **P-value**. | | **Confidence** | How sure Pushwoosh is that the uplift reflects a real difference rather than chance, as a percentage. The higher it is, the stronger the evidence. | | **P-value** | How likely this difference would appear by chance if messaging had no real effect. The lower it is, the stronger the evidence. | | **Z-score** | How far apart the two groups performed. Close to 0, the difference could be random. The further from 0, the more confident you can be that one group is better. | | **Group breakdown** | Treatment and Control side by side: **Estimated users**, **Total number of events**, **Events per user**, and **Conv. rate (unique)**. | ## How to read the results * **Significant, positive Uplift:** your messaging is driving this event. Keep sending it. * **Significant, negative Uplift:** the control group converts better than the treatment group on this event. Before concluding messaging is working against you, check whether this event is one messaging is actually meant to drive. * **Not significant:** the difference isn’t reliable yet, not that there’s no effect. Let the event accumulate more conversions before judging it. * **Not enough data:** either group has fewer than 10 conversions or fewer than 10 non-conversions for this event. Compare an event with more volume, or wait for more of it to happen. Two more things to keep in mind: * **Compare within one measurement window.** Reshuffling, resizing, rescoping, changing the mode, an Auto-refresh rollover, or an Experiment end [restarts the window](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/#compare-events). Results from before and after one of these aren’t comparable. * **Pick events your campaigns are meant to drive,** such as a purchase or a sign-up, rather than events messaging has no direct path to. --- # Control groups FAQ **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/control-groups-faq/ Answers to common questions about control group membership, modes, country and tag scope, and how campaign results are measured. --- ## Can I hold out users for just one journey instead of my whole app? Yes. Use the [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) element inside Customer Journey Builder to hold out a share of users at one point in a single journey, instead of holding out users across every marketing send of the app. It reports its own goal uplift, separate from this page. ## Can an application have more than one control group? Yes. Click **Add control group** in **Settings > Control groups** to add another one, for example one per market. * Every enabled group applies to every marketing send of the app, each within its own countries and tag. You don’t pick a group per message. * A user is held out if any group selects them. * Groups can overlap. Pushwoosh warns you and shows **Overlaps with N groups** in the list, because users who match both groups skew the statistics of both. See [how several control groups work together](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-several-control-groups-work-together). ## Can I delete the control group? Yes, from the three-dot menu on the group’s page. This cannot be undone: the group stops holding users out, and all its statistics are lost. Other groups of the app keep working. If no enabled group is left, marketing messages go to all users. To stop the group and keep its history, click **Disable instead** in the delete dialog, or [disable it](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#disable-a-control-group) from the three-dot menu. ## Can I rename a control group? Yes. On the group’s page, click the **three-dot menu** and select **Rename**. Only the name you see in Pushwoosh changes: the users in the group, its current cycle, and its statistics stay the same. ## What users are in the control group? Pushwoosh doesn’t keep a stored list of control group members. It uses a formula, a calculation that always gives the same answer for the same inputs, based on your app, the user’s ID, and the group’s current settings. This applies to your entire existing user base the moment you enable the group, not only to new sign-ups, and the result stays the same for a given user until you [reshuffle](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group) the group or change its size, the user’s ID changes, or an Auto-refresh cycle ends or an Experiment reaches its end date. ## Do control group users receive transactional messages? Yes. [Transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) (e.g. password resets, order confirmations) are delivered to all users, including the control group. Only marketing messages are blocked for control group users. ## Where do I set the marketing or transactional message type? You choose it wherever you send messages. For each channel, see [Where to set the message type](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#where-to-set-the-message-type). ## What happens to campaigns created before I enabled the control group? Campaigns created before you enabled the control group work exactly like new ones: Pushwoosh checks a campaign’s message type at send time, not at creation time. * **Marketing message** (or no message type at all, which can happen for campaigns created [via the API](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#api-requests-and-message-type)): the control group excludes those users starting immediately. * **Transactional message**: control group users keep receiving it as before. Edit the campaign and set the message type to **Transactional message** if you want it to reach everyone, including the control group. ## Can I change the control group size after enabling it? Yes. Open the group’s page from **Settings > Control groups**, click **Control group settings**, choose a new percentage (1% to 20%), and click **Apply**. Pushwoosh recomputes membership for your whole user base against the new percentage: growing the group adds users without removing anyone already in it, and shrinking it releases some existing members. This can affect comparison with earlier campaigns, so change the size only when needed. ## What control group modes are available? Three. **Permanent** keeps the same group until you turn it off. **Auto-refresh** replaces it with a new group automatically at the end of each cycle (1, 2, or 3 months), returning the previous group to messaging. **Experiment with an end date** runs the group until a date you set (at least 30 days out), then returns everyone to messaging automatically. Set the mode when you create the group, or later in **Control group settings**, the same dialog used to [edit the group’s size and countries](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#edit-the-control-group-size-and-countries). Changing the mode closes the group’s current cycle, the same as resizing or rescoping it. ## How do I see a control group’s past cycles? 1. Open the group’s page. 2. Click the date-range selector in the top right. It’s labeled **Current cycle** for an **Auto-refresh** group, **Current experiment** for an **Experiment with an end date** group, and has no label for a **Permanent** group. 3. Pick a cycle under **Previous**. Ended cycles are listed by number, as **Cycle** or **Experiment**. A Permanent group can have past cycles too, because resizing, rescoping, or changing the mode starts a new one. ## Why did my control group results start over? The measurement window restarts whenever the group’s members or audience change: * you [resize the group or change its countries or tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#edit-the-control-group-size-and-countries); * you [reshuffle](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group) the group; * you [change the mode](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode); * an Auto-refresh cycle ends or an Experiment reaches its end date. Data from before that point no longer matches the current group, so it isn’t included. Ended cycles stay available under **Previous** in the date-range selector on the [group’s page](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/). ## What happens when an Experiment with an end date ends? On the **To** date you set, everyone in the group automatically returns to marketing messages. The experiment then appears under **Previous** in the date-range selector on the [group’s page](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/), named **Experiment** with its own number, so you can still review its results. ## Can I scope the control group to specific countries? Yes. Set **Countries** when you create the group, or later: open the group’s page from **Settings > Control groups** and click **Control group settings**. Leave it empty to hold out users across your whole audience, or select one or more countries to hold out only devices there. Country scope is a per-device check on top of the percentage formula: it doesn’t change who the formula selects, only which of their devices get excluded from marketing. Changing **Countries** restarts the [uplift measurement](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/), since the compared audience changes. ## What happens to a device with no country data? It’s never held out, even when **Countries** is set and its user is otherwise selected. Pushwoosh checks a device’s system **Country** tag against the scope, and a device without that tag has nothing to check against, so it keeps receiving marketing messages. ## Can I scope the control group by tag? Yes. Set **Tag** when you create the group, or later: open the group’s page from **Settings > Control groups** and click **Control group settings**. Choose any string or boolean tag except **Country**, then choose one or more values. It works like country scope: a per-device check on top of the percentage formula, combined with **Countries** if you set both. A device with no value for the tag, or a value you didn’t include, keeps receiving marketing messages even if its user is otherwise selected. ## How does tag scope interact with device and user tags? A tag you set per device only ever describes that one device, so scoping by it holds out devices one at a time. A tag you set per user is copied onto every device of that user, so scoping by it holds out or spares all of that user’s devices together. Either way, a device with no value for the scope tag keeps receiving marketing messages, and which devices are held out can change over time as a user’s tag value changes, without anyone editing the control group. ## What happens if I disable the control group? That group stops holding out users, and they start receiving marketing messages again unless another enabled group holds them out. If no enabled group is left, marketing messages go to all users. The group keeps its settings and statistics history. If you enable the control group again later without reshuffling, you get back the exact same group, because membership is computed by the same formula. To get a different group after re-enabling, [reshuffle it](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group). ## What does Reshuffle do? **Reshuffle control group** draws a brand-new random sample at the same percentage, replacing the current control group with a different set of users. It’s available from the **three-dot menu** on the group’s page. Use it when the same users have been excluded from marketing for a long time and you want fresh holdout coverage. Reshuffling also restarts the [measurement window](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/) for control group analytics. ## How do I see which users are in the control group? Pushwoosh doesn’t tag control group users. Open the group’s page from **Settings > Control groups**, click the **three-dot menu**, and select **Export users** to download a CSV of current members. **Export users** requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. To check membership programmatically instead, call [`CheckControlGroupMembership`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#checkcontrolgroupmembership) from the Control Groups API with a batch of user IDs. ## Why might a user in the control group still receive marketing messages? Pushwoosh computes control group membership by **User ID**. If the same person has several contact methods (e.g. email, push device) that are not linked under one User ID, Pushwoosh treats them as different users and computes membership for each separately: one may land in the control group while another does not. A contact method with no User ID at all is never held out, since Pushwoosh needs a User ID to compute membership. To avoid this, [register all contact methods with the same User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/). ## What control group percentage should I choose? The right percentage depends on your goals, app size, and how you plan to use the results. A smaller percentage (e.g. 3%) keeps more users in the audience and may suit teams that prioritize reach. A larger percentage (e.g. 10%) can give stronger statistical confidence when comparing groups. You can set a **Custom** value between 1% and 20% to match your needs. ## Why isn’t my control group exactly the percentage I set? The group size shown on the page is the actual number of users the hash formula assigned to the group, not the percentage multiplied by your user count. On smaller apps this count can visibly differ from the percentage, since the formula distributes users individually rather than sampling an exact share. The count is cached and refreshed about once a day. You can trigger an immediate recalculation from the page, which shows the previous count with a loading indicator while it runs. ## What does **Not enough data** mean in the events table? **Not enough data** means that either the treatment or control group had fewer than 10 conversions or fewer than 10 non-conversions for that event during the selected time window. Pushwoosh cannot determine whether the difference between the groups is statistically significant until more data is available. ## How is **Uplift** calculated? **Uplift** shows the percentage difference between the treatment and control conversion rates, relative to the control rate. A positive value means the treatment group converted better. Pushwoosh calculates it as `(conv. treatment − conv. control) / conv. control × 100%`. ## What does **Conversions driven by messaging** mean? **Conversions driven by messaging** is the estimated number of additional events generated by your messages beyond the number expected without messaging. Pushwoosh calculates it as `(events per user in treatment − events per user in control) × treatment group size`. The percentage shows these additional events as a share of all treatment events. ## Which events can I compare and how many? Events set as [Conversion Goals in Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) appear in the table automatically. Click **Add events to compare** to add any other event, up to 10 events in total. Message-engagement events (e.g. opens, clicks, sends, bounces) are excluded even if set as a goal, since control group users never receive marketing messages to react to. --- # Create and manage a control group **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/ Create a control group with its name, size, mode, and scope, then edit, rename, reshuffle, export, disable, or delete it. --- Create control groups for your app, then change their settings or remove them later from each group’s page. ## Create a control group Create a control group to start holding out a share of your users from marketing messages. An app can have several groups, for example one per market. Other groups keep working as they are when you add one. [Learn how several control groups work together](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-several-control-groups-work-together) ### Open the Add control group dialog 1. Navigate to **Settings > Control groups**. 2. Click **Add control group**. ![Control Groups list with one group, its Audience column, and an arrow pointing to the Add control group button](https://docs.pushwoosh.com/control-groups-list.webp) ### Name the group In **Name**, enter a name that tells the group apart from the others, for example the market it measures. The name is required and must meet these rules: * Up to 64 characters long. * No colon (`:`). * Unique within the app. You can [rename the group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#rename-the-control-group) later. ### Set the group size Set the **Group size**: the share of users held back from marketing messages, from 1% to 20%. ![Group size field in the New control group dialog](https://docs.pushwoosh.com/control-groups-3.webp) ### Choose a mode Mode sets how long the same users stay in the group, and what happens after that. * **Permanent:** the group is selected once and stays excluded from messaging until you turn it off. * **Auto-refresh:** a new group is selected automatically at the end of each cycle, and the previous one returns to messaging. Set **Refresh every** to **1 month**, **2 months**, or **3 months**. * **Experiment with an end date:** the group runs until the **From**–**To** dates you set, then everyone automatically returns to messaging. **To** must be at least 30 days after **From**. ![Mode options in the New control group dialog](https://docs.pushwoosh.com/control-groups-4.webp) To see how each mode appears on the group’s page, see [Analyze control group impact](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/). ### Scope the control group by country Optional. Country scope limits which devices are held out. Pushwoosh first selects users by the group size, then holds out only those of their devices that are in the countries you choose. In **Countries**, select the countries, or leave it empty: * **Empty Countries:** every user the percentage formula selects is held out, regardless of location. * **Non-empty Countries:** only devices whose system **Country** tag matches one of the selected countries are held out. A device outside the selected countries keeps receiving marketing messages, even if its user is one the percentage formula selected. ![Countries field in the New control group dialog](https://docs.pushwoosh.com/control-groups-5.webp) How country scope works per device * **No Country tag, not held out:** a device without a system **Country** tag keeps receiving marketing messages, even if its user is selected and **Countries** is set. Pushwoosh needs the tag to check the device. * **Per device, not per user:** a person with devices in different countries can have some held out and others not. For example, with **Countries** set to United States of America, their United States device is held out, and their United Kingdom device keeps receiving marketing messages. ### Scope the control group by tag Optional. Tag scope works like [country scope](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#scope-the-control-group-by-country) above, but by tag value: of the users selected by the group size, only devices with the tag values you choose are held out. If you set both, Pushwoosh applies **Countries** and **Tag** together. 1. In **Tag**, select a string or boolean tag. **Country** doesn’t appear in this list. Scope by country uses **Countries** above instead. 2. Choose one or more values that put a user in scope. A boolean tag offers **true** and **false**. A string tag suggests existing values and also accepts your own. ![Tag field in the control group dialog: a string tag such as a subscription plan, with two values selected](https://docs.pushwoosh.com/global-control-group-11.webp) * **Empty scope tag:** every user the percentage formula selects is held out, regardless of the tag. * **Non-empty scope tag:** only devices whose value of that tag is one of the values you chose are held out. A device with a different value, or no value at all for that tag, keeps receiving marketing messages, even if its user is one the percentage formula selected. How tag scope works per device * **No tag value, not held out:** a device with no value for the scope tag keeps receiving marketing messages, the same as a device without a **Country** tag. * **Device tag:** each device is checked on its own, so some of a user’s devices can be held out and others not. * **User tag:** the value is copied to all of the user’s devices, so either all of them are held out or none are. * **Tag value changes:** if a user’s tag value changes, their devices move in or out of scope on the next check. The user stays in the group, and you don’t need to edit it. A user joins or leaves the group’s audience whenever their tag value changes, and statistics count only users who match the scope today. Scope by a tag that rarely changes, such as market or signup cohort, so the group measures the same audience over time. The tag scope, together with **Countries**, applies everywhere the group works: **Group size** counts only users in scope, messages are held back only from devices in scope, and [Export users](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#export-control-group-users) includes only users in scope. ### Check overlaps with other groups If the countries and tag values you chose match users of another enabled group, the dialog shows a warning that names the overlapping groups. You can still create the group. Users who match both groups are held out by each of them, so the statistics of both groups are skewed. To avoid an overlap, choose countries that the other group doesn’t cover, or different values of the same tag. Two groups don’t overlap when: * Both groups are scoped to countries and share none. * Both groups are scoped by the same tag and share no value. A group with empty **Countries** and no tag covers all users, so it overlaps with every other enabled group. ### Review and create the group Check the settings and click **Create**. **Create** stays disabled until the name is valid and, if you selected a tag in **Scope the control group by tag**, until you choose at least one value. The group’s page opens, and the group starts excluding its members from marketing messages right away, following the mode you chose. You can change the size, mode, countries, and tag later from the group’s page. See [Manage the control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#manage-the-control-group). ## Manage the control group **Settings > Control groups** lists every group of the app with its **Audience**: **All users**, or the countries and tag values the group is scoped to. An enabled group whose audience matches other enabled groups shows **Overlaps with N groups** under its name. Click the group’s row to reach its own page, where the sections below apply. ### Edit the control group size and countries You can adjust the size of a control group, and the countries it holds out, at any time from its page. 1. Click **Control group settings** in the top right. The button also shows the group’s current percentage, for example **Control group settings: 10%**. 2. Choose a new percentage: * **3% (Maximum reach):** smallest exclusion group for sensitive or high-value audiences. * **5% (Recommended for most apps):** optimized mix between measurement confidence and user reach. * **10% (Higher confidence):** larger control group for clearer and more reliable ROI validation. * **Custom (1%–20%):** set a custom control group size for flexible measurement. ![Percentage options in the Control group settings dialog](https://docs.pushwoosh.com/global-control-group-12.webp) 3. Optional: change the [mode](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode). ![Mode options in the Control group settings dialog](https://docs.pushwoosh.com/global-control-group-13.webp) 4. In **Countries**, select one or more countries to limit the control group to, or leave it empty to hold out users across your whole audience. ![Control group settings dialog with the Countries field showing United States of America and United Kingdom selected](https://docs.pushwoosh.com/global-control-group-4.webp) 5. Optional: change the [tag scope](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#scope-the-control-group-by-tag). If you select a tag, **Apply** stays disabled until you choose at least one value. 6. If the dialog warns that the audience overlaps with other groups, [check the overlap](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#check-overlaps-with-other-groups) before you continue. 7. Click **Apply** to confirm changes. ### What changes when you edit the group Each setting affects the group and its results differently. #### Group size Membership is recalculated for your whole user base, not only for new users: * Growing the group adds users until it reaches the new percentage. No one already in it is removed. * Shrinking the group removes just enough members to reach the new percentage. This makes results harder to compare with earlier campaigns, so change the size only when necessary. #### Other settings * **Mode:** the group’s current cycle closes and a new one starts. The users held out stay the same until the next reshuffle, resize, or Auto-refresh rollover. * **Countries:** the [uplift measurement](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/) restarts, because a different audience is now compared. The users in the group stay the same. Only which of their devices are held out from marketing changes. * **Tag scope:** the uplift measurement restarts, the same as when you change **Countries**. ### Rename the control group Renaming changes only the name you see in Pushwoosh. The users in the group, its current cycle, and its statistics stay the same. 1. Click the **three-dot menu** in the top right. 2. Select **Rename**. 3. Enter the new name and click **Save**. The same [name rules](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#name-the-group) apply as when you create a group. ### Recalculate the group size **Group size** shows a saved number that Pushwoosh updates about once a day. Right after you change the group size, or when a lot of new users join your app, this number can be out of date. To update it right away, click **Recalculate** next to the group size. ### Reshuffle the control group **Reshuffle control group** picks a new random set of users, keeping the same percentage. Everyone currently in the group leaves it, and a different set of users takes their place. Use it when the same people have been left out of your marketing messages for a long time. 1. Click the **three-dot menu** in the top right. 2. Select **Reshuffle control group**. ![Three-dot menu with Reshuffle control group option on a control group's page](https://docs.pushwoosh.com/global-control-group-9.webp) 3. Confirm in the dialog that appears. Important Reshuffling changes which users are excluded from marketing and restarts the measurement window for [control group analytics](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/): data from before the reshuffle no longer reflects the current group and isn’t included in later comparisons. ### Export control group users Exporting control group users requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. Pushwoosh doesn’t tag control group users. Export from this page for a one-off list, or call [`CheckControlGroupMembership`](https://docs.pushwoosh.com/developer/api-reference/control-groups-api/#checkcontrolgroupmembership) to check a batch of user IDs programmatically. The file includes only the group’s users within its [countries](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#scope-the-control-group-by-country) and [tag scope](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#scope-the-control-group-by-tag). 1. Click the **three-dot menu** in the top right. 2. Select **Export users**. 3. When the export finishes, click **Download CSV** to save the file. ![Exporting complete notification with Download CSV button on a control group's page](https://docs.pushwoosh.com/global-control-group-6.webp) ### Disable a control group 1. Click the **three-dot menu** in the top right. 2. Select **Disable control group**. ![Three-dot menu with Disable control group option on a control group's page](https://docs.pushwoosh.com/global-control-group-5.webp) Disabling stops that group from holding out any users immediately, and they start receiving [marketing messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) again, unless another enabled group holds them out. If no enabled group is left, marketing messages go to all users. The group keeps its settings and statistics history. If you re-enable it later without reshuffling, Pushwoosh reproduces the exact same group, since membership is computed by the same formula. To get a different group after re-enabling, [reshuffle it](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#reshuffle-the-control-group). Segments built on the PW_ControlGroup tag Pushwoosh doesn’t create or update the `PW_ControlGroup` tag. Values left in it from before August 25–26, 2026 don’t show real membership, even if your control group stayed enabled. * If segments, exports, or automations use this tag, rebuild them from [Export users](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#export-control-group-users). * When you disable a group, Pushwoosh also clears the remaining tag values in the background. ### Delete the control group Deleting a control group removes it and its configuration permanently. The group stops holding users out, and all its statistics are lost. Other groups of the app keep working. If no enabled group is left, marketing messages go to all users. 1. Click the **three-dot menu** in the top right. 2. Select **Delete group**. 3. In the dialog that appears, click **Delete** to confirm. This cannot be undone. Keep the history: disable instead To stop an enabled group without losing its statistics, click **Disable instead** in the same dialog, or [disable the group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#disable-a-control-group) from the three-dot menu. --- # Understanding events in Pushwoosh **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/ Learn how to track user events in Pushwoosh for behavior-based messaging, audience segmentation, and campaign optimization. --- **Events** are intended to record various actions a user performs in the app. Unlike Tags that store flat data (such as country, device model, or installation data), event data is stored historically. This creates a history of user behavior rather than static device information. All contextual information about the event can be passed as attributes and their values. Pushwoosh events fall into three categories: [Default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) available out of the box, [Custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) you define for your app, and [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) for tracking revenue in a unified format. Once properly collected, this data can be used to: * Trigger behavior-based messages. * Build segments of users who perform a specific action in the app. * Get insights about user flow, usage metrics, and other statistical data. ## Implementation ### 1. Create events in Pushwoosh Control Panel You don’t have to create a custom event before your app sends it, because Pushwoosh creates it the first time it arrives. See [Events created automatically](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#events-created-automatically). Create the event here first when you want to choose the attribute types yourself or build segments and journeys on it before the first event arrives. To create an event, go to the **Events** section of your Control Panel and press the **Add event** button. Enter the name and description and add the [attributes](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#2-set-attributes) if needed. Tip Event data is valuable only if it’s submitted properly. Therefore, we strongly recommend following the simple guidelines below: * Try to name events as short as possible. * Name events after actions a user performs in the app - “LoggedIn”, “SignedOut”, “Subscribed”, etc. * Event names are not case-sensitive: `LoggedIn` and `loggedin` are the same event. Spaces count, so `LoggedIn `with a trailing space is a different event. * Merge events that describe a single action into a single event. For example, use “Subscribed” event with proper attribution rather than separate “SubscribedToSports”, “SubscribedToPolitics”, “SubscribedToTechnology” events. ![Events section showing Add event button and event creation interface](https://docs.pushwoosh.com/events-README-1.webp) ### 2. Set attributes All details describing an event are provided with its attributes. Attributes are key/value pairs. In the Control Panel you can choose one of these types: * **integer** * **string** * **boolean** * **list** * **decimal (price)** * **date** * **version** * **time zone** Note You can add up to 100 attributes to an event, with the payload (content) of each attribute limited to 10 KB. Once an event has 100 attributes, new ones are not created and their values are not recorded. ![Event attributes form with two attributes, total_price of type decimal (price) and currency of type string, and Add attribute](https://docs.pushwoosh.com/events-README-2.webp) For example, you can track user logins and collect login credentials (e.g., logged in with Facebook, Google, Twitter, or email), internet connection type (wifi, cellular, unknown), and whether it was successful or not (true or false values) with a single Login event by specifying its attributes as follows: | attribute | type | value | | ---------- | ------- | -------------------------------- | | type | string | facebook, google, twitter, email | | connection | string | wifi, cellular, unknown | | success | boolean | true, false | This event will be recorded in Pushwoosh with attribute values and a timestamp, so later it can be used for building segments, targeting messages, and collecting stats. Tip To filter users by event attribute values when building a segment, see [Build segments by events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#event-attribute-operators-by-type). ### 3. Call /postEvent API Developer assistance needed For this step, you’ll need assistance from your development team. Please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events) with them for detailed instructions on event setup. ## Events created automatically When your app or server sends an event with a name that Pushwoosh doesn’t know yet, Pushwoosh creates the event right away. This works for every application and for every way of sending events: `postEvent` from the iOS, Android, and Web SDKs, and the `/postEvent` API call from your server. An event name that is new to Pushwoosh must be at most 128 characters long and can’t contain control characters. Pushwoosh rejects an unknown event that doesn’t meet this and doesn’t create it. Pushwoosh creates: * **The event** with the name as sent. The description is `Autogenerated Event`. * **An attribute for each key** in the event, with a type taken from the value that arrives first: | Value sent | Attribute type | | ------------------------------------- | --------------- | | `12` | integer | | `12.5` | decimal (price) | | `"12"` | string | | `true` or `false` | boolean | | `["gold", "pvp"]` | list | | `"2026-10-07"` or another date string | date | | `"1.2.3"` | version | A version also needs the right attribute name when the value has two or four parts. `"2.5"` becomes a version only if the attribute name mentions a version, build, or release, such as `app_version` or `build_number`. Otherwise it becomes a string. Pushwoosh never creates a **time zone** attribute automatically. A value such as `"Europe/Berlin"` becomes a string. To get a time zone attribute, create the event and the attribute yourself before the first event arrives. An empty value or `null` doesn’t create an attribute. Pushwoosh creates it when the first non-empty value arrives. The type stays fixed after that, and later values that don’t fit that type are not recorded. For example: * If a level number first arrives as the string `"12"`, the attribute becomes a string, and you can’t compare it as a number in a segment. * A Unix timestamp arrives as a number, so it becomes an integer, not a date. Tags follow different rules. `setTags` looks at what a string contains, so `"12"` sent as a tag becomes an integer. See [How to set up a custom tag](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags#how-to-set-up-a-custom-tag). Create the event yourself in **Events** before the first one arrives when: * You want to set the attribute types, for example integer for a level or date for an expiry time. * You want to build segments and journeys on the event before your app sends it for the first time. * You want a meaningful description instead of `Autogenerated Event`. Auto-created attributes count toward the [attribute limit per event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#2-set-attributes). To check that an event was created, open **Events** and find it in the list. To see the events one user sent, open [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) and the **Events History** tab. If an event your app sends doesn’t appear in **Events**, check that it goes to the right application, then contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). ## Importing events The **Import events** feature allows you to upload event data from a CSV file, making it easy to track and analyze user activity in bulk. You can import events from various analytics and CRM systems to enhance your data and improve segmentation. To import events, navigate to **Audience** → **Import CSV** → **Import events**. ![Upload events](https://docs.pushwoosh.com/importevents.webp) ### Step 1. Upload CSV Upload a CSV file containing event data. The file must include the following required columns: * **Event name:** the name of the event. * **Event date:** the date and time when the event occurred. * **HWID or User ID:** a unique identifier for the user associated with the event. You can also include additional columns for optional event attributes to enhance your data. To upload your file, drag and drop the CSV into the upload area or click to select a file from your device. The maximum file size allowed is 100 MB. ### Step 2. Map columns After uploading the file, you must map the CSV columns to the corresponding event attributes in Pushwoosh. * Assign each required field (Event Name, Event Date, HWID, or User ID) to the correct column in your file. * If your CSV contains custom attributes, map them to meaningful names that align with your data tracking needs. * Click **Add attribute** to include extra fields if needed. Once all columns are correctly mapped, click **Import events**. Once the import is successfully completed, a confirmation message will be displayed. You can now use these events to maximize the potential of your campaigns. If needed, you can upload another file to continue importing additional data. You can find the imported events in the **Events History** section of [**User Explorer**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). ## Delete events To delete several events at once: 1. In the **Events** section, click **Bulk actions** → **Delete…**. 2. Select the events. 3. Click the **Delete** button above the list. Events that campaigns, segments, or integrations use are skipped. Conversion settings of the deleted events are deleted with them, and the dialog asks you to confirm that. Deletion can’t be undone. --- # Conversion events **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/ Choose which events count as conversions in Pushwoosh and track their revenue with PW_Conversion or your existing events. --- Conversion events are the events that count as conversions for your campaigns. Each of them can also track revenue, which Pushwoosh collects in one uniform format for attribution, analytics, and segmentation. ## What are conversion events Revenue events rarely come in one shape. Your SDK might fire `PW_InAppPurchase`, your backend might send a custom `OrderPlaced` event, and Stripe or Shopify deliver their own webhook events. Without a shared format, targeting or reporting on revenue means writing separate logic for every source. Pushwoosh solves this with `PW_Conversion`, a built-in event with a fixed set of fields: * `value`: the transaction amount * `currency`: ISO 4217 currency code * `transaction_id` and `product_id`: optional identifiers `PW_Conversion` covers purchases, subscription renewals, and payments from third-party platforms, regardless of where the original money event comes from. Once revenue lands as `PW_Conversion`, RFM segmentation, Customer Journeys, dashboards, and ManyMoney AI all read the same normalized data, no matter which source produced it. On this page, conversion events means `PW_Conversion` plus the [default](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) and [custom](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) events you choose to count as conversions. ### Conversions and revenue Every conversion event is counted on the [Conversions tab](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#conversions-tab) of **Project overview**, where you see which campaigns, journeys, and in-apps led to it. An event you choose is counted in one of two ways: * **Conversion only:** Pushwoosh counts how many times the event happened after your messages, for example `signup_completed` or `trial_started`. * **Conversion with revenue:** Pushwoosh also reads the price and currency from the event’s attributes and records the amount as `PW_Conversion`. `PW_Conversion` is always on the list and always tracks revenue. You can’t remove it. ### How revenue reaches Pushwoosh You get a `PW_Conversion` record in one of three ways: * **Send it directly from your code:** your app or backend sends `PW_Conversion` after a purchase. * **Track revenue for an existing event in the Control Panel:** point Pushwoosh at the price and currency attributes of a purchase event you already send, and it generates `PW_Conversion` records without changing your code. You can track revenue for more than one event per application. * **Send `__amount` and `__currency` attributes on any event:** the first time such an event arrives, Pushwoosh adds it to your conversion events with revenue tracking already on, with no setup in the Control Panel. See [Automatic revenue mapping](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#automatic-revenue-mapping). Important Use only one path per transaction. If you send `PW_Conversion` directly and also track revenue for another event that fires for the same purchase, Pushwoosh records both as separate, independent `PW_Conversion` events. Pushwoosh does not detect or merge duplicates across sources, even when they share a `transaction_id`. ## Use cases Once conversion events are set up, that data becomes usable everywhere revenue matters in Pushwoosh: * See which campaigns, journeys, and in-apps lead to conversions and how much revenue they bring on the [Conversions tab](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#conversions-tab) of **Project overview**. * Build [RFM segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/) for high-value users based on spend amount and recency, without custom revenue logic for each event. * Set `PW_Conversion` as a [Conversion Goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) in a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) to see which flows actually drive purchases, not just clicks or opens. * View revenue in dashboards and reporting without building custom logic for each purchase event. * Combine revenue from SDK purchase events and payment webhooks (Stripe, Shopify) into one dataset instead of analyzing each source separately. * Let [ManyMoney AI](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) use real transaction data in its recommendations and campaign optimizations. Example scenarios **Combine renewals and store orders** A subscription app sends `PW_Conversion` after each renewal payment, and tracks revenue for its Shopify order event for one-time merchandise sales. Both revenue sources now count toward the same RFM segmentation, so the app’s biggest spenders land in the **Champions** segment automatically. From there, you can target them with a loyalty offer in a Customer Journey. **Unify spending across channels** A mobile app sends `PW_Conversion` for in-app purchases directly from its backend, and separately tracks revenue for a Stripe webhook event for purchases made on its website. Because Pushwoosh treats both as the same kind of revenue event, ManyMoney AI sees a customer’s full spending history, app and website combined, when it recommends who to target next. **See which journeys drive purchases** A journey targets recently churned users with a win-back offer. Its **Conversion Goal** is set to `PW_Conversion`. After the journey ends, the goal statistics show how many of those users made a purchase, so you can see the journey drove real purchases, not just opens. To see how much revenue that represents, check the segment or dashboard revenue view separately. Customer Journey shows purchase counts by goal, not a dollar total per journey. ## How to set up conversion events Conversion events are configured per application. Revenue can come from your code, from events you already send, or from both. Follow the instructions for the path that fits your implementation. ### Send `PW_Conversion` from your code This path fits when you can add or change event code in your app or backend. After each purchase, send `PW_Conversion` through the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) method. Its revenue reaches RFM segmentation, journeys, and dashboards without any setup in the Control Panel. With only `PW_Conversion`, the **Conversions** tab of **Project overview** shows the **See which campaigns lead to conversions** banner instead of statistics. To see conversions on this tab, choose at least one other event as described below. Developer assistance needed To send `PW_Conversion` from your code, you’ll need help from your development team. Share the example below, along with [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/) with them for instructions. Add the `postEvent` call wherever a purchase completes in your app or backend. **Example for JavaScript:** ```javascript Pushwoosh.postEvent("PW_Conversion", { value: 49.99, currency: "USD", transaction_id: "ord_18274", product_id: "sku_premium_m" }); ``` Once you’ve set up conversion events in the Control Panel, samples for JavaScript, Swift, Objective-C, and Java are also available under **View code** on the **Conversion events tracking** banner in **Audience > Events**. #### Attributes for the postEvent call The following table lists the attributes you can pass when sending `PW_Conversion`. | Field | Type | Required | Description | | ---------------- | ----------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `value` | number | Yes | The monetary amount of the transaction. | | `currency` | string (ISO 4217) | Yes | The transaction currency code, for example `USD` or `EUR`. | | `transaction_id` | string | No | A unique identifier for the transaction. Recommended for your own record-keeping. Pushwoosh stores it as-is but does not use it to deduplicate revenue on its own. | | `product_id` | string | No | The identifier of the purchased product or plan. | Caution Conversion events do not currently support refunds or cancellations. There is no way to send a negative or reversing `PW_Conversion` record. Once a transaction is recorded, it stays in your revenue totals even if the purchase is later refunded or canceled. ### Choose conversion events and track their revenue This path fits when conversion or purchase data already flows through events you send, and you do not want to change your code. You can choose up to 100 events per application, not counting `PW_Conversion`. If you select more, **Save** fails with an error. Note Choosing an event as a conversion does not change or replace it. It keeps working exactly as before in segmentation, event history, and dashboards. When the event tracks revenue, Pushwoosh additionally creates a `PW_Conversion` record from it, used only for revenue features. 1. Go to **Audience > Events** and click **Set up events** on the **See which campaigns lead to conversions** banner. You can also start from the **Conversions** tab in **Project overview**. ![See which campaigns lead to conversions banner on the Events page with the Set up events button highlighted](https://docs.pushwoosh.com/events-conversion-events-8.webp) 2. In **Choose conversion events**, select at least one event that counts as a conversion and click **Next**. ![Choose conversion events step with three events selected, price and currency attributes shown for Purchase and CheckoutSuccess, and the Next button highlighted](https://docs.pushwoosh.com/events-conversion-events-9.webp) Events that already carry a price and a currency are listed first, and the **Price and currency found** column shows their attribute names. Pushwoosh looks for such events once a day. 3. In **Conversion events list**, set up revenue for the events that carry a price. `PW_Conversion` is listed first with the **Default** badge and always tracks revenue. For events with a price and currency found, Pushwoosh fills in those attributes for you, and they’re saved with the list. You don’t need to open the dialog for them. ![Conversion events list step with PW_Conversion marked Default, Purchase and CheckoutSuccess tracking revenue, and Track revenue highlighted for 1st purchase](https://docs.pushwoosh.com/events-conversion-events-10.webp) To set up revenue for any other event, click **Track revenue** (or **Edit revenue** to change it) and fill in the **Revenue tracking for event** dialog: * In **Price**, select the attribute that stores the transaction amount. * In **Currency**, select the attribute that stores the currency code. * Optionally, select the **Transaction ID** and **Product ID** attributes. ![Revenue tracking for event dialog for 1st purchase with order_total as Price, currency_code as Currency, order_id as Transaction ID, and product_id as Product ID](https://docs.pushwoosh.com/events-conversion-events-11.webp) Click **Apply**. Events without revenue still count as conversions. 4. Click **Save**. To change the list later, click the **Conversion events settings** (gear) icon on the **Conversion events tracking** banner in **Audience > Events**. There you can: * Add another event with **Add event**. * Stop tracking revenue for an event with **Stop revenue tracking…** in the event’s actions menu. It still counts as a conversion. * Remove an event with **Remove event…** in the event’s actions menu. It no longer counts as a conversion. Important Changes don’t create `PW_Conversion` records for past events. When you add an event, change its revenue attributes, or stop its revenue tracking, the **Conversions** tab recalculates that event over the last 10 days and today, so its conversions and revenue in that window change to match the new setup. Removing an event doesn’t change past statistics. ## Monitor conversion events Once you’ve chosen at least one event, the **Conversion events tracking** banner on **Audience > Events** summarizes conversion activity for the selected application: * How many conversion events track revenue, including `PW_Conversion`. * How many count as conversions only. * How many times all conversion events fired in the last 7 days. ![Conversion events tracking banner showing 3 revenue, 1 conversion, and hits in the last 7 days, with View code, Conversion events settings, and Conversions dashboard buttons](https://docs.pushwoosh.com/events-conversion-events-12.webp) From the banner, you can: * Click the **View code** icon to open the `PW_Conversion` integration samples. * Click the **Conversion events settings** icon to edit the list. * Click **Conversions dashboard** to see which campaigns led to conversions on the [Conversions tab](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#conversions-tab) of **Project overview**. --- # Custom events **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/ Create custom events in Pushwoosh to track user actions, improve app analytics, and build targeted marketing campaigns. Complete guide with examples and implementation steps. --- Unlike [default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events), which are universal across many apps and sectors, custom events are specific to your app. These events track specific actions unique to your needs, such as completing a workout or extending a subscription. They help you understand how users interact with your specific features. Also, they provide deeper insights into user behavior, help [track retention](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics), and allow for the creation of highly personalized and effective marketing campaigns. This ultimately improves user engagement and satisfaction. If a custom event carries revenue data (for example, `order_completed` with a price attribute), you can track its revenue as a [conversion event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) without adding new code. ## Creating custom events To create a custom event in Pushwoosh: 1. Go to **Audience > Events** and click **Create Event**. In the drop-down menu that appears, select **Custom Event**. ![Create Event dropdown menu showing Custom Event option](https://docs.pushwoosh.com/events-custom-events-1.webp) 2. In the window that opens, enter the name of your event in the **Event Name** field. For example, use level_completed to track when a user completes a game level. Provide a brief description of the event in the **Description** field. This helps quickly identify the event’s purpose. ![Custom event creation form with Event Name and Description fields](https://docs.pushwoosh.com/events-custom-events-2.webp) 3. Define the specific attributes relevant to your event and select the attribute type. Event attributes are specific pieces of information that describe the details of an event. They capture various aspects of a user’s interaction within your app. This provides valuable data for precise message targeting and segmentation. You can add up to 100 attributes to an event. Available attribute types: * **integer** * **string** * **boolean** * **list** * **decimal (price)** * **date** * **version** * **time zone** For details on attribute types, see [Understanding events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#2-set-attributes). ![Event attributes configuration showing attribute types and fields](https://docs.pushwoosh.com/events-custom-events-3.webp) For each event that occurs, there will be corresponding attribute values. For example, attributes for the **level_completed** event could include **level_number, completion_time, score,** or **points_earned**. When the event is recorded in Pushwoosh, it will include these attribute values along with a timestamp. This data can later be used to build segments, target messages, and collect statistics. After filling in the necessary details and adding attributes, click **Create** to finalize the event setup. If you decide not to proceed with creating the event, click **Cancel** to discard the changes. Developer assistance needed Once you’ve set a custom event in the Control Panel, you’ll need help from your development team to integrate it into your app. Share the integration code you can find next to the event you created, along with [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for instructions. ## Managing custom events The events list provides an overview of all custom events you’ve created for your application. Each event helps you track specific user interactions or behaviors. **Event Details** * **Event Name** that identifies the event (e.g., “button_clicked”, “video_watched”) * **Attributes** or key pieces of information associated with the event, providing additional context (e.g., “button_id”, “video_category”) * **7-Day Activity** - the number of times the event occurred in the past seven days, offering a quick view of recent engagement ![Custom events list showing event details and management options](https://docs.pushwoosh.com/events-custom-events-4.webp) Each event has a menu for further management: * Click **View Code** to access the code snippet required to integrate this event into your app’s codebase. * Click **Event Statistics** to dive deeper into detailed statistics for the chosen event, providing insights into user behavior. * Click **Edit Event** to modify an event’s settings, including its name and associated attributes, to ensure accurate data collection. ![Event management menu with View Code, Event Statistics, and Edit Event options](https://docs.pushwoosh.com/events-custom-events-6.webp) ## Using custom events in Pushwoosh Once you’ve set up your events, you can use them to: 1. [Create a customer journey](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project) that will launch at your custom event, using the [**Trigger-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element. 2. [Build segments by events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/), including filtering by event attribute values and operators. 3. Segment users by criteria such as how many times they performed a custom event or when the event last occurred. Use these segments to set up [Audience-based entries](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) to customer journeys or use the [Segment Split option](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) within the journey. 4. Set a [time delay in your customer journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#delay-based-on-user-or-event-data) based on the custom event users trigger within their journey. 5. Use the custom event in the [Wait for Trigger step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) to set different communication scenarios based on whether a user triggers this particular event or several events within a specified time. ## Custom events use cases | App Type | Custom Event | Description | Use Case | | ----------------------------- | ----------------- | ------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Fitness and Health apps | workout_complete | A user completes a workout session. | Send motivational messages or rewards to keep users engaged and encourage regular workouts. | | E-commerce apps | cart_abandoned | A user adds items to their cart but does not complete the purchase. | Send a reminder email or push notification with a special offer or discount on the items left in the cart.[Explore recommended events for e-commerce apps](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-e-commerce) | | Education and e-Learning apps | complete_course | A user completes a course. | Send congratulatory messages and recommend advanced courses or related topics. | | Media and entertainment apps | watch_video | A user watches a video. | Send notifications about similar content or new releases. Recommend other videos.[Explore recommended events for media apps](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-media) | | Travel and hospitality apps | book_flight | A user books a flight. | Send travel reminders, packing tips, and recommendations for activities at their destination. | | Subscription apps | start_trial | A user starts a free trial. | Send onboarding messages with tips, offer assistance, and highlight key features.[Explore recommended events for subscription apps](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-subscription-management) | | Mobility or carsharing apps | ride_completed | A user completes a carsharing ride. | Send a follow-up in-app message, request feedback, or offer a discount on the next ride. | | Gaming apps | level_completed | A user completes a game level. | Send congratulatory messages and offer tips or rewards for completing the next level.[Explore recommended events for gaming apps](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps) | ## Tracking custom events ### Events statistics You can track each event you’ve created. For this: 1. Navigate to **Audience > Events**. 2. In the **Events** list, find the event you want to track and click on the three dots next to it. Select **Event Statistics**. 3. Set the date range for which you want to analyze the event data. 4. To count only events with specific attribute values, click **Attribute**, then pick the attribute, an operator, and a value. 5. In the dropdown next to the date range, choose how to group the chart: **Hourly**, **Daily**, or **Weekly**. The chart shows how many times the event was triggered in each hour, day, or week of the range. For a range of one or two calendar days, the chart groups events by hour at first. For three days or more, it groups them by day. Days and weeks follow the timezone shown next to the date range, and weeks start on Monday. When the range starts or ends mid-week, the first or last week counts only the days inside the range. ![Event statistics page with a chart of event triggers over the selected date range](https://docs.pushwoosh.com/events-custom-events-7.webp) To download the chart data as a CSV file, click **Export data**. The file has one row per point on the chart: the start time of the hour, day, or week, written in UTC, and the number of events. **Export users list** is available only with the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission. To download the list of users who sent the event, click it. Pushwoosh prepares a CSV file in the background. When the file is ready, click the **Download CSV** button that appears on the page. The file contains: * **Rows:** one row for each time a user sent the event. If a user sent the event five times, the file has five rows for that user. * **Columns:** the time of the event, the User ID, the HWID, the platform, and one column for each event attribute. * **Dates:** all events from the selected dates, including the whole last day. * **Filters:** the **Attribute** filter doesn’t apply, so the file includes all events. To reload the data for the same range, click **Refresh**. ### Events brought by messages Event Statistics counts every event your app sent. To see which events followed a particular message, open the statistics of that message or of a journey. There, the **Events** table lists the events that users sent after clicking the message. An event is counted for the last message the user clicked in the 72 hours before the event. For example, a user taps a push and buys something two hours later: the purchase event appears in that push’s statistics. ![Events table in the statistics of a push message, with ProductView, AddToCart, and Purchase and their Hits and Unique counts](https://docs.pushwoosh.com/statistics-push-notification-statistics-19.webp) * If the user clicked several messages in that window, the event is counted for the last one only. * If the user clicked no message in that window, the event isn’t counted for any message. It still counts in Event Statistics. * A click is a tap on a push, opening an App Inbox message, a click on a link in an email, or reading a WhatsApp message. Opening an email or clicking its unsubscribe link doesn’t count. SMS messages have no clicks, so events are never counted for them. Where to find these events: * **A single message:** the **Events** table on the **Timeline** tab of [push](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#custom-events-after-a-tap-on-this-push), [email](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#custom-events-after-a-link-click-in-this-email), [App Inbox](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/#custom-events-after-opening-this-message), or [WhatsApp](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#custom-events-after-a-whatsapp-message-is-read) statistics * **A journey:** the [Events block](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#events-brought-by-journey-messages) in the journey statistics, broken down by step ### Dashboards Pushwoosh also allows you to track custom events using the Events dashboard, which contains statistics related to the events used in your campaigns, or by building your own custom dashboard. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) ### Retention tab Use the **Retention** tab to track retention related to specific events relevant to your business. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics) --- # Default events **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/ Out-of-the-box events for any industry and use case --- Note Available starting from SDK 5.2.2 ## What are default events Default Events are basic interactions that users perform in apps or websites regardless of their industry or functionality. These key user actions form the core of customer communication and can be leveraged for any product and customer at any stage of the customer lifecycle. Default Events are available out of the box for up-to-date SDK versions. They don’t need additional settings except for PW_InAppPurchase (see the details below). A Default Event appears in **Audience → Events** automatically the first time the SDK sends it. You don’t need to activate it to start collecting data. You can also activate a Default Event to add it to the list before the SDK sends it. This lets you build a segment or a Customer Journey before the first data arrives. To activate a Default Event: 1. Navigate to the **Events** tab within the **Audience** section. 2. Click **Create Event**. 3. Select **Default Event** from the drop-down menu. 4. In the pop-up window, choose the desired event and click **Activate**. ![Default Events dropdown menu showing available events to activate](https://docs.pushwoosh.com/events-default-events-1.webp) Use Default Events in behavior-based messaging. For example, use them as a [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) in Customer Journey or a trigger for In-App messages. Tip To see how you can use Default Events to create a marketing campaign in Pushwoosh and automatically convert free users into paid customers, watch this tutorial: [Youtube Video: How you can use Default Events to create a marketing campaign in Pushwoosh and automatically convert free users into paid customers](https://youtube.com/watch?v=Nlv2l8kfyxU) ## Device and app usage ### PW_DeviceRegistered This event fires when a device without a push token receives one. The SDK requests the token when the app calls `registerForPushNotifications`, so this is the first app launch only if the app registers for push notifications at launch. On iOS the same call shows the permission prompt. In iOS and Android apps, a device gets a push token even if the user doesn’t allow notifications, so the event fires for these users too. It fires again if a device that lost its token gets a new one. Replacing an existing token with a new one doesn’t fire it. **Event attributes** * **platform** (string): the platform name of the registered device. * **platform_id** (integer): the platform code, same value as **device_type** in [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice). **Use cases** * Launch an onboarding campaign for newly registered users. Add the Trigger-based Entry using PW_DeviceRegistered Event to an Onboarding [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). * Identify acquisition activities that bring you more newcomers with the Event statistics. * Engage users right at their first visit. Meet them with [In-Apps](https://docs.pushwoosh.com/product/messaging-channels/in-apps/?utm_source=docs&utm_medium=post&utm_campaign=default_events) triggered by the DeviceRegistered Event. ![In-app message triggered by DeviceRegistered event for onboarding new users](https://docs.pushwoosh.com/events-default-events-2.webp) ### PW_DeviceUnregistered This event fires when a device loses its push subscription. That happens when: * The push service reports the token as invalid, for example after the app is uninstalled. * The app unregisters the device through the SDK. * A user unsubscribes from emails. To sync these devices to your own database, see [Sync unregistered devices to your system](https://docs.pushwoosh.com/product/integrations/event-streaming/#sync-unregistered-devices-to-your-system). **Event attributes** * **message_id** (string) * **message_code** (string) * **campaign_id** (string) * **platform** (string) * **platform_id** (integer) **Use cases** * Retain users when they decide to stop using your app. For example, you can send an [email](https://docs.pushwoosh.com/product/messaging-channels/emails/) with a special offer to every user who has uninstalled the app. * Maintain communication with users who have unsubscribed from one of your channels. For example, if a user doesn’t want to receive push notifications anymore, you can send an [email](https://docs.pushwoosh.com/product/messaging-channels/emails/) or show an [in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) encouraging them to use the app more often. ### PW_PushAlertsChanged *For mobile apps only* This event fires when a user turns push notifications for your app off or on in the device settings. It’s available for iOS, Android, Huawei, and Amazon devices. Pushwoosh records a change in both directions: from enabled to disabled (opt-out) and from disabled to enabled (opt-in). You don’t need to activate this event. It appears in your app the first time it fires and isn’t listed in the **Default Event** drop-down menu. You can’t delete it in the Control Panel. **When Pushwoosh records the change** The SDK reports the notification permission when the app starts. A user who turns notifications off in the device settings is recorded only on the next app launch, which can be hours or days later. The event time is when Pushwoosh received the change, not when the user changed the setting. The event requires a mobile SDK that reports the permission: * iOS: starting from iOS SDK 5.7.2 * Android and Amazon: starting from Android SDK 5.7.5 * Huawei: starting from Android SDK 6.1.0 The event doesn’t fire in these cases: * The device registers for the first time. Pushwoosh saves the permission from the first registration as the starting state, so the user’s first answer to the permission prompt, allow or deny, doesn’t fire the event. * The user unsubscribes inside your app, or the device is unregistered. Use [PW_DeviceUnregistered](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceunregistered) for these cases. On Android 13 and later, your app can register the device without showing the permission prompt and show the prompt later. In this case, the device is registered with notifications disabled. If the user allows notifications at the later prompt, the event fires with **new_value** equal to `true`. If the user denies them, the event doesn’t fire, because the permission stays disabled. **Event attributes** * **old_value** (boolean): the previous permission state. `true` means notifications were enabled. * **new_value** (boolean): the new permission state. `false` means the user turned notifications off (opt-out), `true` means the user turned them on (opt-in). * **reason** (string): what set the new state. * **source** (string): the API method that reported the change, such as `registerDevice`, `applicationOpen`, `multiRegisterDevice`, or `setTags`. * **token_in_request** (boolean): whether the request included a push token. Always `false` for `applicationOpen`. * **request_ts** (integer): Unix time when Pushwoosh received the request. * **prev_app_open_at** (integer): Unix time of the previous app launch before the change. Together with **request_ts**, it shows the time window in which the user changed the setting. Included only if known. * **app_version** (version): the app version. Included only if the request contains it. * **os_version** (version): the OS version of the device. Included only if the request contains it. The **reason** attribute takes one of these values: | Value | What it means | | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | `sdk` | The Pushwoosh SDK reported the permission from the device. Use this value to track changes made by users. | | `api_field` | The permission was sent in an API request from your server or a data import. | | `set_tags` | The **Push Alerts Enabled** [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) was changed directly, for example, with `setTags`. | | `default_true` | The device has a push token but the request didn’t include the permission, so Pushwoosh set it to enabled. | **Use cases** * Win back users who turned notifications off. Start a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) with a [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on PW_PushAlertsChanged with **new_value** equal to `false` and reach them by email, SMS, or an [opt-out recovery popup](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/#opt-out-recovery-popup). * Create [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) of users who disabled notifications within a period. Filter by **reason** equal to `sdk` to keep only changes made by users on their devices. * Welcome users who turned notifications back on with a push that highlights what they missed. * Track how often users opt out after specific campaigns in the [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) and event statistics. ### PW_ApplicationOpen *For mobile apps only* ApplicationOpen event is triggered **every time a user launches your mobile app** on their device. **Event attributes** * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **application_version** (string): the app version. **Use cases** * Gather user retention stats based on the ApplicationOpen event statistics. * Run in-app messaging campaigns for users who are active in the app. ### PW_ScreenOpen *For mobile apps only* This event fires every time a user views a **specific screen in your mobile app**. **Event attributes** * **application_version** (string): the app version. * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **screen_name** (string): technical screen name obtained from your app. **Use cases** * Send perfectly relevant messages to users who showed their interest in specific app sections by starting Customer Journeys with the PW_ScreenOpen event. * Evaluate and improve effectiveness of your promotions. [A/B/n test](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) two or several versions of your offer, wait for the ScreenOpen event to take place, and then see which content has performed best. * Get to know what app screens are visited the most. Filter the Event statistics by screen name. ### PW_ApplicationMinimized *For mobile apps only* This event fires when a user minimizes (but not force stops) the app on their device. **Event attributes** * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **application_version** (string): the app version. **Use cases** * Track the average session time between ApplicationOpen and ApplicationMinimized events. * Send push notifications while the app runs in the background to grab users’ attention and return them to the app. ### PW_UserIdle *For mobile apps only* Developer assistance needed To start receiving PW_UserIdle events, you’ll need help from your developer team. Share the SDK setup notes below. This event fires when a user is inside your app but has not interacted with the screen for a configurable period. Unlike PW_ApplicationMinimized, which triggers after the user leaves the app, PW_UserIdle fires while the user is still in the app. It captures the moment when attention drops, the user hesitates, or gets distracted. Use it as a server-side signal to show a timely In-App, offer assistance, or nudge the user toward the next step. Idle detection is disabled by default on both platforms. When the timeout key is missing or set to `0`, no PW_UserIdle events are sent. To enable it, set the timeout (minimum 30 seconds) in your app’s platform configuration: * **iOS:** add the `Pushwoosh_IDLE_TIMEOUT_SECONDS` key (Number, minimum 30) to your app’s Info.plist. * **Android:** add the `com.pushwoosh.idle_timeout_seconds` meta-data (integer, minimum 30) to your **AndroidManifest.xml**. **Event attributes** * **screen_name** (string): the screen where the user went idle. On Android, formatted as `ActivityName/FragmentName` when the current screen is nested. Otherwise, the activity name. On iOS, formatted as `ParentVC/ChildVC` when nested. Otherwise, the view controller name. * **idle_seconds** (integer): the configured idle timeout that elapsed. * **session_duration** (integer): seconds since the app was last brought to the foreground. * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **application_version** (string): the app version. **Behavior** * Fires at most **once per foreground session**. Returning to the background and back to the foreground starts a new session. * The timer pauses automatically while the soft keyboard is open, while a modal dialog or system prompt is on screen, and while the app is in the background. * Physical interaction with the screen (touches, scrolling, and typing) resets the timer. **Use cases** * Trigger an In-App offer or in-context assistant when users linger on a high-intent screen (cart, pricing, checkout) without taking action. * Surface a hint or tutorial when users stall on an onboarding step. * Identify where users lose momentum by filtering idle event statistics by `screen_name`. ### PW_ApplicationExit *For mobile apps only* Developer assistance needed To start receiving PW_ApplicationExit events, you’ll need help from your developer team. Share the SDK setup notes below. This event fires when a user backgrounds the app and does **not return within a configurable timeout**. Unlike PW_ApplicationMinimized, which fires immediately when the user leaves, PW_ApplicationExit fires only after the configured delay has elapsed. It confirms that the user has genuinely left the session rather than briefly switching apps. Use it to re-engage users who have abandoned a session mid-flow, or to trigger follow-up campaigns after a confirmed exit. Exit intent detection is disabled by default. To enable it, set the timeout (10–30 seconds, values outside this range are clamped automatically) in your app’s platform configuration: * **Android:** add the `com.pushwoosh.exit_intent_timeout_seconds` meta-data (integer, 10–30) to your **AndroidManifest.xml**. * **iOS:** add the `Pushwoosh_APPLICATION_EXIT_TIMEOUT_SECONDS` key (Number, 10–30) to your app’s **Info.plist**. **Event attributes** * **screen_name** (string): the last screen active when the user backgrounded the app. * **session_duration** (integer): seconds since the app was last brought to the foreground. * **exit_intent_seconds** (integer): the effective timeout that elapsed before the event fired (10–30 s). * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **application_version** (string): the app version. **Behavior** * The timer starts when the app is backgrounded and is **cancelled** if the user returns before the timeout expires. * Screen name and session duration are captured at the moment of backgrounding, not when the event fires. * The event does not fire if exit intent detection is disabled (timeout key is `0` or not set). **Use cases** * Re-engage users who abandoned a purchase or sign-up flow. Trigger a push notification or email with a reminder or discount. * Start a win-back Customer Journey for users who exit without completing a key action (e.g., leaving a cart or stopping mid-onboarding). * Identify high-drop-off screens by filtering event statistics by `screen_name`. ## Web ### PW_SiteOpened *For web only* The SiteOpened event fires every time a user opens any of your website pages. **Event attributes** * **device_type** (integer): device type code. Refer to [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) for details. * **title** (string): the title of the page visited. * **url** (string): the URL of the page visited. * **path** (string): the path of the page visited, without the domain, query string or fragment, decoded to readable form, with the letter case kept. For example, `https://example.com/Catalog/Shoes/?size=42#reviews` is sent as `/Catalog/Shoes/`. Events sent by WebSDK 3.102.0 to 3.104.0 have `path` in lowercase. **Use cases** * Analyze your site traffic distribution and compare the number of times users open specific site pages. For this, filter the Event stats by its attributes. * Follow up on a user’s website visit with a relevant communication. Build a journey starting with the SiteOpened event set up for a specific `title` or `url` attribute. * Target visitors of a site section. Build a segment with the SiteOpened event where the `path` attribute **starts with** `/Catalog/`. Enter the value in your site’s letter case, because the SDK sends `path` that way. Enter paths with non-English letters as they read, for example `/café/`, not `/caf%C3%A9/`. A segment or condition saved with a lowercase `path` needs the value entered again. ![Website traffic analytics showing page visit statistics and user behavior patterns](https://docs.pushwoosh.com/events-default-events-4.webp) ## Commerce ### PW_InAppPurchase *For mobile apps only* Developer assistance needed To set up the In-App Purchase event, you’ll need help from your developer team. Share the guide with them. The InAppPurchase event fires every time a user purchases an in-app item in your app. Examples include paid subscriptions, game mechanics elements, in-app currency, etc. Note Please keep in mind these items must be specified on your app’s App Store page. To implement the In-App Purchase event: * **iOS:** set the `Pushwoosh_PURCHASE_TRACKING_ENABLED` param of the Pushwoosh iOS SDK to `yes`. * **Android:** configure sending purchase information to Pushwoosh [using this guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50#in-app-purchase-tracking). **Event attributes** * **productIdentifier** (string): obtained from the App Store. * **quantity** (integer): the number of items purchased. * **transactionDate** (string): date of purchase. * **status** (string): status of purchase. **Use cases** * Gather stats of in-app purchases. * Run promotions and keep track of their success. Set the Wait for Trigger event following the promo message. ![Customer journey configuration showing Wait for Trigger step with In-App Purchase event](https://docs.pushwoosh.com/events-default-events-5.webp) Tip You can track revenue for PW_InAppPurchase as a [conversion event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) without adding new code, to use its revenue in RFM segmentation, journey attribution, and ManyMoney AI. ## Push notifications ### PW_NotificationSend *For mobile and web only* This event fires once a Pushwoosh message is sent to a user’s device. **Event attributes** * **message_id** (string) * **message_code** (string) * **campaign_id** (string) **Use cases** * Track whether a message is sent, regardless of the channel used. * Create user [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on the number of messages sent. * View all messages sent to a user in the [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) and analyze the user’s further behavior. ### PW_NotificationOpen *For mobile and web only* This event fires every time a user opens a push notification sent with Pushwoosh. **Use cases** * Configure the [optimal time to send a push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push#optimal-time-paid-subscription-required). * Create [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on notifications performance within a specific time period. ## Email ### PW_EmailSent *For email only* This event fires once an email is dispatched to the email provider. Check **status_code** for the outcome. **Event attributes** * **message_id** (string): the ID of the message. * **message_code** (string): the unique identifier of the specific email message. * **status_code** (integer): the delivery status code returned by the email provider for this send attempt. * **campaign_id** (string): the ID of the campaign. Included only if the email was sent as part of a campaign. * **campaign_code** (string): the unique code of the campaign. Included when the send is part of a campaign and campaign lookup succeeds. **campaign_code** may be missing even when **campaign_id** is present. **Use cases** * Track dispatch outcomes using **status_code**, separately from whether the email was later opened or clicked. * Create user [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on the number of emails sent. * View all emails sent to a user in the [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) and analyze the user’s further behavior. ### PW_EmailOpen *For email only* This event fires when a user opens an email sent via Pushwoosh. Opens by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t fire it, so journeys, segments, and integrations that use this event don’t react to them. **Event attributes** * **campaign_code** (string): the unique code of the campaign associated with the email. * **campaign_id** (string): the ID of the campaign. * **message_code** (string): the unique identifier of the specific email message. * **message_id** (string): the ID of the message. * **opened_by** (string): who or what triggered the open: `user` for a genuine open by the recipient, or `ampp` for an automated open triggered by [Apple Mail Privacy Protection](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#what-each-step-includes) pre-fetching the tracking pixel. **Use cases** * Track email opens to identify users who actively engage with your emails. Use this data to create follow-up campaigns, such as sending discount codes to users who open promotional emails. * If users open an email but don’t complete the desired action (e.g., making a purchase), you can trigger follow-up emails with reminders or personalized offers. * Analyze open rates across campaigns to refine your subject lines and email timing strategies for campaigns like product launches or seasonal promotions. * Create a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on **PW_EmailOpen** with **opened_by** equal to `user` to target recipients who opened the email, excluding AMPP-triggered opens. ### PW_EmailLinkClicked *For email only* This event fires every time a user clicks a link in an email. Clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t fire it, so journeys, segments, and integrations that use this event don’t react to them. **Event attributes** * **message_id** (string) * **message_code** (string) * **campaign_id** (string) * **redirect_link** (string) * **original_link** (string) **Use cases** * Run targeted campaigns for users who click on a specific link in an email. * Send another message to users who ignored the link in the first email to encourage them to click on it (or take other action). ### PW_EmailSubscribed *For email only* This event fires when a user subscribes to one or more email categories, from a [preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) submission or resubscribe link, and also on auto-subscription when a new email device first registers. **Event attributes** * **message_id** (string): the ID of the message that triggered the subscription, if applicable. * **campaign_id** (string): the ID of the associated campaign, if applicable. * **message_code** (string): the unique identifier of the specific message, if applicable. * **categories** (array of strings): the names of the email categories the user subscribed to; omitted if none apply (e.g. registration with no auto-subscribed categories). * **resubscribe** (boolean): `true` for any preference center or resubscribe-link submission — including a user’s first subscription made there; omitted for auto-subscription on device registration. **Use cases** * Use **resubscribe** to isolate preference-center-driven subscriptions from auto-subscription on registration. * Create [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) of users subscribed to a specific category. ### PW_EmailUnsubscribed *For email only* This event fires when a user unsubscribes from one or more email categories, and also on a full opt-out from all marketing emails (**Unsubscribed Emails** tag) — in that case **categories** is omitted. If a user unsubscribes from several categories at once, **categories** lists all of them. **Event attributes** * **message_id** (string): the ID of the message the user unsubscribed from, if applicable. * **campaign_id** (string): the ID of the associated campaign, if applicable. * **message_code** (string): the unique identifier of the specific message, if applicable. * **categories** (array of strings): the names of the email categories the user unsubscribed from; omitted on a full opt-out. **Use cases** * Create [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) of users who opted out of a specific category to exclude them from future campaigns for that category. * Trigger a win-back journey for users who unsubscribe from a category. Tip In segment builder, the **categories** attribute dropdown for **PW_EmailSubscribed** and **PW_EmailUnsubscribed** lists your account’s actual email categories instead of raw recorded values. See [segment by category](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#segment-by-category) (requires the [preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) enabled). ## In-app messages ### PW_InAppShown *For mobile apps only* This event fires when an in-app message is displayed to the user. **Event attributes** * **message_code** (string): the unique identifier of the specific message associated with the in-app. * **message_id** (integer): the ID of the message. * **campaign_code** (string): the unique code of the campaign associated with the in-app. * **campaign_id** (integer): the ID of the campaign that triggered the in-app. * **rich_media_code** (string): the unique identifier of the rich media content displayed in the in-app. * **inapp_code** (string): the unique code identifying the specific in-app message shown. **Use cases** * Track the frequency of in-app message views to adjust your messaging strategy, such as removing redundant messages that might annoy users. * Use in-app impressions data to evaluate the reach of promotional campaigns like flash sales or subscription upgrades. * If an in-app message is shown but not clicked, you can trigger a follow-up action, such as sending a push notification with similar content to reinforce the message. ### PW_InAppClicked *For mobile apps only* This event fires when a user interacts with an in-app message by clicking on it. **Event attributes** * **message_code** (string): the unique identifier of the specific message associated with the in-app. * **message_id** (integer): the ID of the message. * **campaign_code** (string): the unique code of the campaign associated with the in-app. * **campaign_id** (integer): the ID of the campaign that triggered the in-app. * **rich_media_code** (string): the unique identifier of the rich media content displayed in the in-app (if applicable). * **inapp_code** (string): the unique code identifying the specific in-app message clicked. * **element_id** (string): the identifier of the specific element within the in-app message that was clicked (e.g., a button or link). * **link** (string): the URL or deep link associated with the clicked element. **Use cases** * Analyze click behavior to identify which buttons or links users interact with most often and optimize future in-app message designs. For example, test different CTAs in subscription upgrade campaigns. * Trigger automated actions based on user clicks, such as redirecting users to a specific product page or sending a push notification. ## SMS ### PW_SMSSent *For SMS only* This event fires once an SMS is dispatched to the SMS provider. Check **status_code** for the outcome. **Event attributes** * **message_id** (string): the ID of the message. * **message_code** (string): the unique identifier of the specific SMS message. * **status_code** (integer): the delivery status code returned by the SMS provider for this send attempt. Use it to tell success from failure. * **campaign_id** (string): the ID of the campaign. Included only if the SMS was sent as part of a campaign. * **campaign_code** (string): the unique code of the campaign. Included when the send is part of a campaign and campaign lookup succeeds. **campaign_code** may be missing even when **campaign_id** is present. **Use cases** * Track dispatch outcomes using **status_code**, regardless of the channel used elsewhere in the same journey. * Create user [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on the number of SMS messages sent. * View all SMS messages sent to a user in the [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) and analyze the user’s further behavior. ## WhatsApp ### PW_WhatsAppSent *For WhatsApp only* This event fires once a WhatsApp message is dispatched to the WhatsApp provider. Check **status_code** for the outcome. **Event attributes** * **message_id** (string): the ID of the message. * **message_code** (string): the unique identifier of the specific WhatsApp message. * **status_code** (integer): the delivery status code returned by the WhatsApp provider for this send attempt. Use it to tell success from failure. * **campaign_id** (string): the ID of the campaign. Included only if the WhatsApp message was sent as part of a campaign. * **campaign_code** (string): the unique code of the campaign. Included when the send is part of a campaign and campaign lookup succeeds. **campaign_code** may be missing even when **campaign_id** is present. **Use cases** * Track dispatch outcomes using **status_code**, separately from delivery or read status. * Create user [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) based on the number of WhatsApp messages sent. * View all WhatsApp messages sent to a user in the [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) and analyze the user’s further behavior. ### PW_WhatsAppReceived *For WhatsApp only* This event fires when a WhatsApp message is received through the system. **Event attributes** * **message** (string): the content of the received message. For location messages, contains the coordinates in `"latitude,longitude"` format (e.g. `"55.751244,37.618423"`). * **latitude** (number): the latitude of the shared location. Included only when the user shares their current location. * **longitude** (number): the longitude of the shared location. Included only when the user shares their current location. These attributes are included only if the WhatsApp provider sends them, on the first message of a Click-to-WhatsApp conversation: * **ctwa_clid** (string): the click ID of the Click-to-WhatsApp ad that started the conversation. Use it for conversion attribution — each value is unique per click, so it isn’t useful for segmentation. * **referral_source_id** (string): the ID of the ad or post that started the conversation. When the source is an ad, this is the same ad ID shown in Meta Ads Manager; posts aren’t listed there. * **referral_source_type** (string): the type of the referral source, such as an ad or a post. * **referral_source_url** (string): the URL of the ad or post that started the conversation. * **referral_headline** (string): the headline text of the ad. * **referral_body** (string): the body text of the ad. **Use cases** * Automate follow-up actions in response to incoming WhatsApp messages (such as starting a Customer Journey or tracking user engagement events). * Segment users based on their WhatsApp message interactions to enable targeted messaging or personalized campaigns. * Use `latitude` and `longitude` attributes to trigger location-based journeys when a user shares their location. * Use `ctwa_clid` and `referral_*` attributes to attribute a conversation to the specific Click-to-WhatsApp ad that started it. * Use the [`WhatsApp Ad Click ID` and `WhatsApp Ad Click Date`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) device tags, which persist past the conversation, to create [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags) of users who clicked an ad and later converted. --- # Recommended events for e-commerce **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-e-commerce/ Track and react to user behavior with easy-to-integrate recommended events for e-commerce apps --- Below, you’ll find a bunch of in-app events recommended for your app category. We based our choice on industry-specific best practices and experts’ advice. By tracking these selected events, you can improve your behavioral segmentation and perform more effective, user-centric messaging campaigns. Developer assistance needed You’ll need your developer team’s help to set up these events. Please share [this guide](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#e-commerce) with them for detailed instructions. To implement any of these or other events: * [Create the Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel) in your Pushwoosh Control Panel, add attributes if needed. * Ask your development team to integrate the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method into your mobile project. Provide the event name and attributes names exactly the same as in the Control Panel. For detailed instructions, please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#e-commerce) with them. ## Product viewed Fire this event when a user opens a product page. Use it for browse abandonment campaigns and to segment users by the products and categories they view. Recommended attributes: * product_id: String * product_category: String * price: decimal (price) * source: String ## Product added to cart Fire this event once a user adds product to their cart to build Abandoned Cart campaigns, set user tags, or analyze the efficiency of your promotions. Recommended attributes: * product_id: String * price: decimal (price) * source: String ## Checkout started Fire this event when a user starts checkout. A user who started checkout but didn’t place an order is the audience for a checkout reminder. Recommended attributes: * cart_id: String * cart_total: decimal (price) * currency: String * items_count: Integer ## Order placed Fire this event when a user completes a purchase. Send it for every purchase as your main purchase event, together with Discounted purchase, Product category, or First purchase when they also apply. Order placed works as a trigger for post-purchase messages and as a goal or exit condition for abandoned cart and browse abandonment journeys. Recommended attributes: * order_id: String * __amount: decimal (price) * __currency: String, a currency code such as `USD` * product_id: String * coupon_id: String With the `__amount` and `__currency` attributes, Pushwoosh starts tracking revenue for this event automatically the first time it arrives. See [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/). Caution Don’t add `__amount` and `__currency` to other events, for example Checkout started or Order refunded. Pushwoosh adds the amount of every event that carries them to revenue, so the same money gets counted twice, and a refund increases revenue instead of reducing it. ## Order refunded Fire this event when an order is refunded, fully or partially. Use it to exclude these users from post-purchase campaigns or to ask why they returned the order. Recommended attributes: * order_id: String * refund_amount: decimal (price) * currency: String * reason: String ## Discounted purchase Trigger this event once a user purchases any product using a discount coupon. Recommended attributes: * product_id: String * coupon_id: String * price: decimal (price) * discount: String ## Product Page abandoned Fire this event when a user leaves a product page without a conversion action. Recommended attributes: * product_id: String * price: decimal (price) * source: String * product_page_id: String ## Product added to wishlist Stay informed of the products that users have saved to their wishlists and create personal offers and promo campaigns. Recommended attributes: * product_id: String * wishlist_id: String * product_price: decimal (price) * source: String * user_id: String ## Product removed from wishlist Fire this event when a user deletes a product from their wishlist. Recommended attributes: * user_id: String * wishlist_id: String * product_id: String ## Product category Send this event when a user purchases a product of a specific category. Recommended attributes: * product_id: String * product_category: String * currency: String ## First purchase Trigger this event once a user makes their first purchase. Recommended attributes: * product_id: String, * category: String * date: Date --- # Recommended events for gaming apps **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/ Track and react to user behavior with easy-to-integrate recommended events for gaming apps --- Below, you’ll find a bunch of in-app events recommended for your app category. We based our choice on industry-specific best practices and experts’ advice. By tracking these selected events, you can improve your behavioral segmentation and perform more effective, user-centric messaging campaigns. Developer assistance needed You’ll need your developer team’s help to set up these events. Please share [this guide](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#gaming-apps) with them for detailed instructions. To implement any of these or other events: * [Create the Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel) in your Pushwoosh Control Panel, add attributes if needed. * Ask your development team to integrate the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method into your mobile project. Provide the event name and attributes names exactly the same as in the Control Panel. For detailed instructions, please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#gaming-apps) with them. ## New level When a user reaches a new game level, trigger the New level event. Recommended attributes: * level_id: String * user_id: String ## Level completed Send this event when a user completes a particular game level. Recommended attributes: * level_id: String * user_id: String ## Virtual currency earned Fire this event when a user’s virtual currency balance tops up. Recommended attributes: * currency_name: String * quantity: Integer * user_id: String ## Tutorial completed Trigger this event when a user completes the in-game tutorial. Recommended attributes: * tutorial_name: String * completion: Boolean ## Achievement unlocked Monitor user engagement with the event fired when a user unlocks a specific achievement. Recommended attributes: * achievement_name: String * level: Integer * user_id: String --- # Recommended events for media **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-media/ Essential media events to track content engagement, user preferences, and optimize audience retention with targeted messaging strategies. --- Below, you’ll find recommended in-app events for your app category. We based our choice on industry-specific best practices and experts’ advice. By tracking these selected events, you can improve your behavioral segmentation. This enables more effective, user-centric messaging campaigns. Developer assistance needed You’ll need your developer team’s help to set up these events. Please share [this guide](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#media) with them for detailed instructions. To implement any of these or other events: * [Create the Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel) in your Pushwoosh Control Panel, add attributes if needed. * Ask your development team to integrate the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method into your mobile project. Provide the event name and attributes names exactly the same as in the Control Panel. For detailed instructions, please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#media) with them. ## Search Send this event when a user searches for any content in your app. Recommended attributes: * search_query: String * category: String ## Content read Trigger this event when a user has read a specific piece of content. Recommended attributes: * category: String * article_id: String * author: String * published_date: Date ## Form submission Track submissions of your in-app forms (for example, Net Promoter Score), the choice of content preferences, and other surveys. Recommended attributes: * form_name: String * url: String ## Content shared Trigger this event when a user has shared a piece of content via social networks, email or other channels. Recommended attributes: * category: String * article_id: String * author: String * published_date: Date * button_id: String * social_media: String ## Content preferences Fire this event when a user chooses a specific topic they are interested in. Recommended attributes: * topic: String --- # Recommended events for mobile apps **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-mobile-apps/ Explore expert-recommended events for mobile apps to improve targeting, retention, and user engagement through behavior-based messaging. --- Below, you’ll find recommended in-app events for your app category. We based our choice on industry-specific best practices and experts’ advice. By tracking these selected events, you can improve your behavioral segmentation. This enables more effective, user-centric messaging campaigns. Developer assistance needed You’ll need your developer team’s help to set up these events. Please share [this guide](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#mobile-apps) with them for detailed instructions. To implement any of these or other events: * [Create an Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#1-create-events-in-pushwoosh-control-panel) in your Pushwoosh Control Panel and add attributes if needed. * Ask your development team to integrate the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method into your mobile project. Provide the event name and attributes names exactly the same as in the Control Panel. For detailed instructions, please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#mobile-apps) with them. ## Log out Trigger this event when users log out of their accounts in your app. Recommended attributes: * user_id: String * date: Date ## Payment method added Fire this event when a user adds a payment method to their account in your app. This includes providing card credentials or linking the account to a payment system. Recommended attributes: * payment_method: String, * user_id: String ## Payment method changed Send this event when a user updates their payment method in the app. Recommended attributes: * user_id: String, * payment_method: String ## Button clicked Track button clicks within the app with this event to improve your analytics, test various communication strategies, and increase relevance of your messages based on customer behavior. Recommended event attributes: * user_id: String * button_link: String ## Application updated Trigger this event every time a user installs an updated version of your app. Recommended attributes: * previous_app_version: String ## OS updated Fire this event when a user updates their device’s OS version to ensure your app is fully compliant. Recommended attributes: * previous_OS_version: String * new_OS_version: String --- # Recommended events for subscription management **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-subscription-management/ Track and react to user behavior with easy-to-integrate recommended events for managing in-app subscriptions --- Below, you’ll find recommended in-app events for your app category. We based our choice on industry-specific best practices and experts’ advice. By tracking these selected events, you can improve your behavioral segmentation. This enables more effective, user-centric messaging campaigns. Developer assistance needed You’ll need your developer team’s help to set up these events. Please share [this guide](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#subscription-management) with them for detailed instructions. To implement any of these or other events: * [Create the Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel) in your Pushwoosh Control Panel, add attributes if needed. * Ask your development team to integrate the [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) method into your mobile project. Provide the event name and attributes names exactly the same as in the Control Panel. For detailed instructions, please share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#subscription-management) with them. ## Paid subscription purchase Send this event when a user has bought a paid subscription plan. Recommended attributes: * subscription_plan_name: String * price: decimal (price) * currency:String * expiry_date:Date ## Subscription renewal Trigger this event once a user renews their subscription plan. Recommended attributes: * subscription_plan_name: String * price: decimal (price) * currency: String * renewal_count: Integer ## Free trial started Trigger this event when a user chooses to start a free trial before getting a subscription. Recommended attributes: * free_trial_name: String * expiry_date: Date ## Subscription canceled Use this event to track in-app subscription cancelations. ## Conversion from Free to Paid Trigger this event once a user converts from using your app for free to a paid subscription plan. Recommended attributes: * subscription_plan_name: String * price: decimal (price) * currency: String * date:Date --- # Feature flags **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/ Roll out app and website features to chosen segments and a percentage of users, and switch them off without a new release. --- A feature flag is an on/off switch for a feature in your app or website. You flip it in the Control Panel, with no new release. For each flag you choose who gets the feature: users in a segment, a percentage of all users, or both. For example, show a new screen to 10% of users first, then raise the percentage. If something goes wrong, you can turn the feature off for everyone with the **Kill switch** button. A flag can also carry properties, such as a button color or a discount limit, that you set per audience. Beta feature Feature flags are in beta. Websites and mobile apps can read flags with the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/), the [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/), and the [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/). ## Before you start * **Limit:** Each application can have one flag that isn’t archived. To run more flags at once, contact your account manager. * **Access:** **Feature Flags** appears in every application you can see. To create and change flags, you need the **Modify applications** [permission](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#available-permissions) or the same permission for this application in its [app-specific permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#app-specific-permissions). * **Developer setup:** A flag affects users only after your developers read it in the app or website code by its key. They need Web SDK 3.94.1 or later, Android SDK 6.16.0 or later, or iOS SDK 7.2.10 or later. In an application with no flags yet, the **Feature flags** page shows ready-made code samples for Web, Android, and iOS. Give them to your developers together with the flag key. Share the [Web SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/), the [Android SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/), or the [iOS SDK guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/) with them. If feature flags are turned off for your account, devices stop receiving flags and your app or website uses the values set in its code. Creating or restoring a flag then fails with the message **Feature flags are not available on your plan**, for example in a Control Panel tab that was opened before the change. ## How a flag decides what each user gets Every flag has a list of rules. Each rule has a segment, a percentage of users that get the flag on (**Rollout, %**), and optional property values. For every device, Pushwoosh checks the rules from top to bottom: 1. The first rule whose segment the device matches decides. A rule with no segment matches every device. 2. The **Rollout, %** of the rule decides whether this user gets the flag on. At 20%, two users in ten get it. 3. A user who gets the flag on receives the property values set in that rule. A user who gets it off receives the default property values. Once a device matches a rule, the later rules are skipped, even when that rule turns the flag off for it. A device that matches no rule gets the flag off. A user gets the same result on every visit. Devices signed in with the same User ID get the same result when they match the same rule, because Pushwoosh checks segments for each device. Pushwoosh identifies a user by User ID, or by device ID when no User ID is set. When you raise the percentage, users who already have the flag keep it and new users join them. ## Create a flag 1. Go to **Audience** → **Feature Flags**. 2. Click **Create feature flag**. ![Feature flags page of an application with the Create feature flag button highlighted and the SDK support block listing Web, Android, and iOS](https://docs.pushwoosh.com/feature-flags-1.webp) 3. Enter the **Key**, for example `new_checkout`. The key is any name you choose, but your developers read the flag in code by this exact key, letter case included. Agree on it with them before you save the flag. 4. Enter a **Name** and, optionally, a **Description** so your team knows what the flag controls. The key follows these rules: * Latin letters, digits, `_`, `.`, and `-`, up to 58 characters. * You can’t change the key later. * You can’t reuse a key that another flag of the application has, even an archived one, or a key that differs from it only in letter case, such as `New_Checkout` and `new_checkout`. ### Add properties Properties are values the app or website reads from the flag, for example the text of a banner or the number of items in a recommendation block. Skip this step if the flag only turns a feature on and off. 1. In **Properties**, click **Add property**. 2. Enter the **Property key**, for example `button_color`. Use Latin letters, digits, and `_`, up to 64 characters. The property key is any name you choose, but your developers read the value in code by this exact key, letter case included. Agree on it with them before you save the flag. 3. Choose the type: **String**, **Number**, **Boolean**, or **JSON**. You can’t change the type after you save the flag. 4. Enter the **Default value**. Users who get the flag off receive this value. A flag can have up to 50 properties. ![Feature flag form with the key, name, description, and two properties: button_color of type String and show_apple_pay of type Boolean](https://docs.pushwoosh.com/feature-flags-2.webp) ### Add rules 1. In **Rules**, click **Add rule**. 2. Enter a **Rule name**, for example `Paying customers`. 3. In **Segment**, choose who the rule applies to. Leave it empty to apply the rule to all users. 4. In **Rollout, %**, enter the percentage of the segment that gets the flag on, from 0 to 100 with up to two decimals. 5. In **Values for users this rule turns on**, change the properties that should differ from the defaults for these users. Properties you leave unchanged keep their default values. 6. To add another audience, repeat the steps. Use **Move up** and **Move down** to change the order, because the first matching rule decides. A flag can have up to 10 rules. Under **Rollout, %**, each rule shows about how many devices it turns on, for example **≈ 1,200 devices**. Pushwoosh multiplies the size of the rule segment by the percentage. For a rule with no segment, it takes all devices of the application. Read the number as the most the rule can reach, because a device that matches an earlier rule stops there. ![Rule 1 of a feature flag: Paying customers for the Purchased something segment at 100%, with button_color set to green and show_apple_pay set to true](https://docs.pushwoosh.com/feature-flags-3.webp) Segments a rule can use A rule segment can contain only tag, location, application, and list conditions, because Pushwoosh checks the flag each time a device asks for it. Segments with event, static, external, last-update, or best-time-to-send conditions don’t appear in the segment picker. A rule also can’t use a segment with a [feature flag condition](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#segment-users-by-flag), because one flag can’t depend on another. Saving such a flag fails. For example, to give a new checkout to all paying customers and to 20% of everyone else, create two rules: | Rule | Segment | Rollout, % | Values for users this rule turns on | | :--------------- | :------------------ | :--------- | :-------------------------------------------------- | | Paying customers | Purchased something | 100 | `button_color` = `green`, `show_apple_pay` = `true` | | Everyone else | — | 20 | `button_color` = `orange` | ### Save the flag Click **Save**. The flag is created as **Active**, and devices receive it the next time they check for flags. ## Manage flags The flag list shows the name, key, status, number of rules, and last update time of each flag. To see archived flags, turn on **Show archived**. To open a flag, click its name. The **SDK support** block at the top of the list and of every flag shows the share of the application’s devices on an older SDK, when their devices report the SDK version. These devices don’t get flags until users update the app. A flag has one of three statuses: | Status | What users get | Actions | | :----------- | :------------------------------------------------------------------------------------------- | :------------------------------------------ | | **Active** | The flag works by its rules. | **Reshuffle**, **Kill switch**, **Archive** | | **Killed** | The flag is off for every user, with default property values. Rules and properties are kept. | **Turn back on**, **Archive** | | **Archived** | Devices no longer receive the flag, so the code uses the values your developers set in it. | **Restore** | ### Change a flag 1. Open the flag. 2. Edit the name, description, properties, or rules. 3. Click **Save**. 4. If you changed the rules or the default property values, the **Review the changes** dialog lists what happens to devices. Check it and click **Save**. The dialog shows one line per change: * **Rollout of a rule:** about how many more devices get the flag, or how many lose it. * **Values of a rule:** about how many devices get the new values, and the old and new value of each property. * **New or removed rule, a different segment, or a new rule order:** where devices move. For example, devices of a removed rule move to the next rule that matches them. * **Default values:** the old and new values that devices with the flag off get. Changes to the name or description alone are saved without the dialog. To roll a feature out gradually, raise the **Rollout, %** of a rule step by step. If someone else saved the flag while you were editing it, the Control Panel doesn’t overwrite their changes and asks you to reload the flag. Click **Reload** and make your changes again. ### Turn a flag off for everyone Use **Kill switch** when a feature causes problems and you need to stop it for everyone: 1. Open the flag and click **Kill switch**. ![Top of an active feature flag page with the Reshuffle, Kill switch, and Archive buttons](https://docs.pushwoosh.com/feature-flags-4.webp) 2. Confirm. Every device gets the flag off with default property values the next time it checks for flags. While your website or app is open, devices get the change within an hour. See how the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/#keep-flags-up-to-date), the [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/#keep-flags-up-to-date), and the [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/#keep-flags-up-to-date) check for flags. To turn the flag back on with its rules as they were, click **Turn back on**. ### Pick a different sample of users Reshuffle places every user in a new group, so the same percentage of users is made up of different people. Use it when you want a fresh sample, for example to start a new test. 1. Open an active flag and click **Reshuffle**. 2. Confirm. Some users who have the flag on lose it and others get it the next time their devices check for flags, while the percentages stay the same. ### Archive and restore a flag Archive a flag when the feature is fully released or removed from the code. An archived flag doesn’t count toward the limit of flags per application, and its key stays reserved. 1. Open the flag and click **Archive**. 2. Confirm. An archived flag can’t be edited. To bring it back, turn on **Show archived** in the list, open the flag, and click **Restore**. A restored flag comes back as **Killed**, so it doesn’t reach users on its own. Click **Turn back on** when you’re ready. ### See who changed a flag The change history shows every saved version of a flag: what changed, who changed it, and when. Anyone who can open the flag can see its history. History is recorded for changes from October 13, 2026. A flag with no recorded changes shows **No changes recorded yet**. For a flag created before that date, the oldest entry shows no list of changes, because there is no earlier version to compare it with. 1. Open the flag. 2. Click **History**. The **Change history** panel lists the versions of the flag, newest first. Each entry shows: * The action, for example **Edited**. * Who made the change: the user’s email, or the API token the change came through, for example **API token “Release pipeline”**, or **API token** for a token with no name. * The date and time. * The version number, for example **v4**. ![Change history panel of a feature flag, newest first: action, user email or API token, date and time, and version](https://docs.pushwoosh.com/feature-flags-5.webp) To load earlier entries, click **Show more**. | Action | What happened | | :------------------- | :------------------------------------------------------------------------------------------------------------------------------------ | | **Created** | The flag was created. | | **Edited** | Someone saved changes to the flag. | | **Rollout changed** | The rollout of one rule was changed through the API or the MCP server. In the Control Panel, a rollout change is saved as **Edited**. | | **Killed** | Someone clicked **Kill switch**. | | **Turned back on** | Someone clicked **Turn back on**. | | **Archived** | The flag was archived. | | **Restored** | The flag was restored from the archive. | | **Users reshuffled** | Someone clicked **Reshuffle**. | Under each entry, the panel lists what changed, as the value before and after: **Name**, **Description**, **Status**, **Properties**, **Rule order**, and every added, removed, or changed rule. For a changed rule, it shows only the fields that differ: name, **Segment**, **Rollout**, or **Values**. A rule with no segment shows **All users**. ## Segment users by flag A feature flag condition in a segment finds the devices that got a flag on, got it off, matched one of its rules, or reached the part of the app the flag controls. Use it to message the users who see a new feature, for example to ask for feedback, or to build a journey around them. 1. Open the segment builder, as described in [How to create segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/). 2. Click **Feature flag** in the list of conditions. 3. In **Select feature flag…**, choose the flag. Archived flags aren’t in the list. 4. Choose the state: * **is on**: devices that got the flag on. * **is off**: devices that got the flag off. * **is in rule**: devices that matched the segment of a rule, whether or not the rule’s **Rollout, %** turned the flag on for them. Choose the rule in **Select rule…**. * **saw the feature (30 days)**: devices that sent at least one impression of the flag in the last 30 days, that is, the app called `logImpression` for this flag key. See [Find users who reached a feature](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#find-users-who-reached-a-feature). High Speed Delivery segments, which can hold only tag conditions, can’t have the **Feature flag** condition. ### Which devices are in each state A device’s state follows the flags it received last and the current status of the flag: * A device that has never requested the flag, for example because its SDK doesn’t read flags, is in none of the states. * After you change the rules or a rule’s **Rollout, %**, a device moves to another state only when it requests flags again. * When you kill or archive the flag, **is on** and **is in rule** become empty within about 10 seconds, without waiting for devices to request flags again. **is off** then holds every device that has ever got the flag. ### Find users who reached a feature To find users who saw the enabled feature: 1. Add **saw the feature (30 days)** for the flag. 2. Add **is on** for the same flag and join the two conditions with AND. **saw the feature (30 days)** matches users who reached the screen or code the flag controls, whether the flag was on or off for them. That’s why you add **is on**: it keeps only the users who got the new version. The result is close but not exact. **is on** reflects the flag a device got last, and its impression may come from an earlier visit when the flag was off. The segment stays empty when the app doesn’t log impressions. To log them, see Log impressions in the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/feature-flags/#log-impressions), [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/feature-flags/#log-impressions), or [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/feature-flags/#log-impressions) guide. Killing or archiving the flag doesn’t empty this state. A device leaves it 30 days after its last impression. ### Use flag segments in journeys A segment with a feature flag condition works in an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) and in a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/). For example, split users who got a new checkout from those who didn’t and send each group its own message. ## Limits The Control Panel and the API enforce these limits when you save a flag: | Limit | Value | | :------------------------------------------------------------------- | :---------------------------------------------------------------------- | | Rules per flag | 10 | | Properties per flag | 50 | | Size of the properties of one flag (keys, types, and default values) | 10,000 bytes | | Nesting depth of a JSON value | 5 levels | | Flags per application | 1, more on request to your account manager. Archived flags don’t count. | | Data one device receives for all flags of the application | 64 KB | ## Manage flags through the API Your developers can create and change flags from their own systems with the [Feature Flags API](https://docs.pushwoosh.com/developer/api-reference/feature-flags-api/), and AI assistants connected through the Pushwoosh MCP server can manage flags with the same operations. --- # Understanding segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/ Create targeted audience segments using tags, events, and filters to build user groups for personalized messaging campaigns. --- [Youtube Video: As a longtime Pushwoosh user, you may remember how we first introduced Segments under the name of Filters. The feature has evolved greatly since then, and here is its up-to-date overview](https://youtube.com/watch?v=tahgIHOUGc0) ## What are segments? Segments are groups of app subscribers who meet the conditions you specify. These are users who have specific tag values assigned to their devices and/or user IDs. Use segments to target audiences via [push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications), [in-app messages](https://docs.pushwoosh.com/product/messaging-channels/in-apps/), [emails](https://docs.pushwoosh.com/product/messaging-channels/emails/), [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview), etc. ## Why use segments? Segments enable you to: * **Target the right users**: Send messages only to users who match your specific criteria * **Personalize campaigns**: Create relevant content for different audience groups * **Improve engagement**: Reach users at the right time with the right message * **Optimize costs**: Avoid sending messages to uninterested users ## How segments work Segments are built using filters to define who should be included: * **Tags:** User attributes like age, language, or subscription status * **Events:** User actions like purchases or app opens * **Existing segments:** Previously created segments that you can reuse and refine * **User ID:** Known users identified by their Pushwoosh User ID * **Geo:** Users who are near a selected point on the map * **Compound filters:** Combinations of several filters and logic operators When combining multiple conditions, use logical operators: * **AND**: Users who meet all conditions * **OR**: Users who meet any condition * **AND NOT**: Users who meet first condition but exclude those who also meet second condition This flexibility lets you create simple or complex audience targeting based on your needs. ## Segment types at a glance Choose the segment type that best fits your use case: | Segment Type | What it does | Best for | | ----------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | | [By tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags) | Target users based on their attributes and profile data | Segmenting by age, language, or subscription status | | [By events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events) | Target users based on their actions and app behavior | Targeting purchasers, users who opened the app, content viewers | | [By existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments) | Combine or refine previously created segments | Reusing complex segment logic, combining multiple segments | | [By User ID](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id) | Target known users or devices by User ID, HWID, email, phone, or push token | Precise targeting for known user lists | | [By geolocation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo) | Target users near a selected point within a defined radius | Location-based campaigns and local offers | | [With compound filters](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters) | Combine multiple filter types using AND/OR/AND NOT logic | Advanced targeting like “purchased AND opened app in last 7 days” | | [Anniversary segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments) | Target users on specific dates each year | Birthday campaigns, app anniversary offers, seasonal promotions | | [From CSV import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import) | Upload a file with user IDs or device IDs | Bulk importing user lists | | [With AI](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai) | Instantly creates segments from your plain-language descriptions using advanced natural language processing | Quick segment creation, intuitive design for new users, time-saving automation | | [RFM segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation) | Classifies users based on Recency, Frequency, and Monetary value of their purchase behavior | Purchase behavior analysis, identifying top spenders, churn risk detection | | [External segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments) | Builds a segment from a SQL query against your own PostgreSQL or BigQuery database | Targeting users tracked in an external CRM, data warehouse, or loyalty system | ## Create your segment To build a segment: 1. Go to the **Segments** section of the Pushwoosh Control Panel (**Audience** → **Segments**). 2. Press the **Create Segment** button to open the segment builder. For other creation methods (AI or import, plus [High Speed Delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/) if it’s enabled for your account), click the chevron next to the button and choose an option from the menu. ![Create Segment split button on the Segments list page](https://docs.pushwoosh.com/segmentation-segments-1.webp) [Explore all segment creation methods ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) ## Manage your segments Keep your segments up-to-date and optimized with the following management options. ### Segments list On the **Segments** page you can search segments, sort by last modified, and create new ones with the **Create Segment** button. The table shows: * **Name:** Segment name with the segment logic (Seglang) under it * **Estimated size:** The calculated size or “Segment not calculated” and the last modified date * **Actions:** Open the three-dot menu to edit conditions, refresh size, export to CSV, view segment usage, copy Seglang, clone, or delete the segment * **Send message:** Click it to open the **Create campaign** dialog, where you choose a campaign type (one-time push or email, or an advanced campaign: push-based, email, in-app, SMS, or WhatsApp). ### Actions with segments These actions are available from the three-dot menu next to each segment in the **Segments** list. Some actions are also available on the segment detail page. #### Edit conditions To change the rules that define a segment, choose **Edit conditions**. Alternatively, open the segment and click **Edit conditions** in the header. #### Refresh segment size Select **Refresh Segment Size** to recalculate audience counts. To refresh several segments at once, click **Bulk actions** → **Refresh size…**, select the segments, and click **Refresh size of** *N* **segments**. The sizes are recalculated in the background. #### Export segment users Export segment data to CSV to analyze user lists, integrate with external systems, or back up segment information. Exporting a segment requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. You can export a segment in two ways: * Select **Export segment users…** in the action menu next to the segment. * Select **Generate CSV for export** from the three-dot menu on the segment detail page. Before export, choose which fields to include in the CSV directly in the Control Panel: 1. Select identifiers in **Data fields**. 2. Select the required items in **Tags**. 3. Click **Export**. ![Export segment users dialog where data fields and tags can be selected before CSV export.](https://docs.pushwoosh.com/segmentation-segments-33.webp) The CSV fields depend on your selection in the export dialog. Example CSV | Hwid | Push Token | Type | Type (humanized) | Age | Application Version | City | Country | Device Model | Email Segment Name | First Install | Gender | In-app Product | In-app Purchase | Jailbroken | Language | Last Application Open | Last In-app Purchase date | Name | OS Version | PW Channels | Push Alerts Enabled | SDK Version | Subscription Segments | Unsubscribed Emails | WishList | | --------- | -------------- | ---- | ---------------- | --- | ------------------- | ------------- | ------- | ------------- | ------------------ | ------------- | ------ | -------------- | --------------- | ---------- | -------- | --------------------- | ------------------------- | ---- | ---------- | ----------- | ------------------- | ----------- | ------------------------ | ------------------- | -------- | | 146…Hl23f | 146…Hl23f | 10 | Safari | 27 | | us, new york | us | Safari 12.0.1 | | 2020-02-19 | | | | | en | 2020-07-15 | | | | | 1 | 3.15.1.0 | | true | | | 01D…3C8 | eeeb2fd…fc3547 | 1 | iOS | 18 | 1.11.1.0 | in, bengaluru | in | iPhone SE | | 2018-11-26 | | outwear_01 | 24 | 0 | en | 2018-11-26 | 2018-11-26 | | | | | 5.8.0.0 | 2D732-BB981, 7EC43-1531A | | | | C90…79ae5 | eQFKk…DwJSv | 11 | Chrome | 36 | | us, chicago | us | Chrome 81 | | 2020-08-24 | | | | | en | 2020-08-24 | | | | | 1 | 3.15.1.0 | | | | #### Copy Seglang Segmentation Language is a syntax to define and apply specific conditions or filters when segmenting your audience. The **Copy Seglang** option allows you to copy the underlying logic of a segment you’ve created. ![Copy Segmentation Language option showing segment logic syntax](https://docs.pushwoosh.com/segmentation-segments-12.webp) The copied Segmentation Language can then be used in API calls, such as the [`createmessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api#createmessage) API method or [API-based customer journey entries](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry), to replicate the same segmentation logic. [Learn how to manage segments with the API ](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/) #### Clone segment Quickly create new segments based on existing ones. Cloning saves time when you need similar audience definitions with slight modifications. ![Clone segment option for creating similar segments](https://docs.pushwoosh.com/segmentation-segments-13.webp) #### View segment usage Before changing or deleting a segment, you can see which customer journeys use it. Choose **View segment usage**. ![Three-dot menu on a segment row with View segment usage option highlighted](https://docs.pushwoosh.com/segmentation-segments-28.webp) The **Segment usage** modal shows a table with the journeys and campaigns that use the segment. ![Segment usage modal listing journeys and campaigns that use the segment with columns for journey name, status, used in steps, and launched date](https://docs.pushwoosh.com/segmentation-segments-27.webp) #### Delete segment To remove a segment, choose **Delete**. ![Delete segment option in the three-dot menu](https://docs.pushwoosh.com/segmentation-segments-29.webp) Deleting a segment does not remove or change user data. Campaigns (customer journeys) that use this segment will be paused, and any messages scheduled to that segment will be cancelled. Before confirming, you can see which journeys use the segment and how they will be affected. ![Delete segment confirmation dialog listing related journeys and the checkbox to confirm impact on campaigns](https://docs.pushwoosh.com/segmentation-segments-30.webp) Check **Confirm that deleting will affect related campaigns** to acknowledge the impact, then click **Delete** to proceed or **Cancel** to close. #### Delete several segments To delete several segments at once: 1. Click **Bulk actions** → **Delete…**. 2. Select the segments. 3. Click the **Delete** button above the list. 4. Enter your login email to confirm. 5. If active campaigns use the deleted segments, select **Confirm that deleting will affect related campaigns**. Pushwoosh stops these campaigns. The dialog skips segments that other content uses, such as presets, popups, other segments, feature flags, or dashboard widgets. ## Advanced features ### High-Speed Delivery segments High-Speed Delivery segments keep updated every 10 minutes, ensuring minimal delay when launching messages. Use these segments when you need the fastest possible message delivery. ### RFM analysis Use recency, frequency, and monetary analysis to identify your most valuable customers and create targeted campaigns for different customer value tiers. [Learn more about RFM segmentation ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/) ### External segments Build a segment from users stored in your own PostgreSQL or BigQuery database, without exporting or re-importing that data. [Learn more about external segments ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/) ### API-based segmentation Developer assistance needed API-based segmentation requires working with API methods and segmentation language syntax. Coordinate with your developer team to implement programmatic segment creation or advanced integrations. Create and manage segments programmatically using API methods. This approach is ideal for automated segment management and integration with external systems. [Learn more about API-based segmentation ](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/) --- # Calculate segment size **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/calculating-segment-size/ Learn how to calculate segment size before sending campaigns. Get accurate subscriber estimates across mobile, web, email, SMS, WhatsApp, LINE, Viber, Kakao, and Telegram channels. --- [Youtube Video: Learn how to calculate the segment size before sending out your push campaigns](https://youtube.com/watch?v=tahgIHOUGc0) ## How to calculate the segment size When planning your next targeted campaign, you may want to get an estimate of how many devices receive your push or see your in-app. Pushwoosh makes it easy to calculate the segment size and see the estimated number of subscribers across different communication channels. After you create a segment, its size is calculated automatically when possible from the conditions you set. Tip If the segment view shows “We don’t know the segment size yet” and “Click Refresh to get the up-to-date segment size,” click the **Refresh** link or **Update segment size** button to run the calculation. When you change the conditions, the size is recalculated for the new criteria. Note The final number of subscribers may change when you launch a campaign due to factors such as subscriber availability or changes in their status. Once the segment size is calculated, the segment view shows a summary card **Included in segment: devices that match the conditions**. The card displays: * **Calculated:** The date of the last calculation, with a refresh icon to run **Update segment size** again. * **Unique users:** Total number of distinct users in the segment. * **Devices:** Counts by channel (mobile, web, email, SMS, WhatsApp, LINE, Viber, Kakao, Telegram). Below the card you see the conditions that define the segment. Use **Update segment size** in the header or the refresh icon next to the calculated date to recalculate. ![Segment size calculation screen showing subscriber counts for mobile, web, email, SMS, WhatsApp, and LINE channels](https://docs.pushwoosh.com/segmentation-calculating-segment-size-1.webp) You can view the segment size in the Segments list, including the number of subscribers for each platform. If the size has not been calculated yet, the list shows “Segment not calculated” and the last modified date. ![Segments list view with segment names, estimated sizes, and subscriber counts for each platform](https://docs.pushwoosh.com/segmentation-calculating-segment-size-2.webp) To refresh the segment size from the Segments list, click the three dots next to the segment name and select **Refresh Segment Size** or **Refresh & Export to CSV**. ### Export to CSV Export this data to a CSV file for further analysis. From the segment detail page, use **Generate CSV for export** in the three-dot menu. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#export-segment-users) The CSV file contains information about each user and Tag values. You can use CSV files to send targeted pushes and emails. You can also analyze audience segments to improve your campaigns. --- # How to create segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/ Learn how to create and build segments using tags, events, existing segments, and advanced filtering techniques. --- Segments allow you to target specific groups of users based on their attributes and behavior. Pushwoosh provides multiple ways to create segments, from simple tag-based filters to advanced compound filters combining multiple conditions. To build a segment, follow these steps: 1. Go to the **Segments** (**Filters**) section of the Pushwoosh Control Panel. 2. Click the **Create Segment** button to open the segment builder. For other creation methods (AI or import, plus [High Speed Delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/) if it’s enabled for your account), click the chevron next to the button and choose an option from the menu. ![Create Segment split button on the Segments list page](https://docs.pushwoosh.com/segmentation-segments-1.webp) ## Ways to create segments Select a guide below to get started: [Build segments by tags ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) [Start with the basics—create segments using user attributes and tag values.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) [Build segments by events ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) [Target users based on specific behaviors and event occurrences with flexible time frames.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) [Build segments by existing segments ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) [Reuse and combine previously created segments to build more complex audiences.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) [Build segments by User ID ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/) [Target known users by their Pushwoosh User IDs and combine them with other filters.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/) [Build segments by geolocation ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo/) [Target users who are near a selected point on the map within a defined radius.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo/) [Build segments by feature flag ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#segment-users-by-flag/) [Target users who got a feature flag on or off, or matched one of its rules.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/#segment-users-by-flag/) [Build segments with compound filters ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters/) [Create advanced segments by combining multiple filter types with logical operators.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters/) [Create anniversary segments ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments/) [Target users on significant dates like birthdays or app anniversaries.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments/) [Import a segment ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/) [Create segments by uploading a CSV file with User IDs or Hardware IDs.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/) [Build segments with AI ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai/) [Use natural language to automatically generate segments with AI assistance.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai/) [Create segments via API ](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#createfilter/) [Build segments programmatically using the createFilter API method for automation.](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api#createfilter/) --- # Create anniversary segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments/ Create segments based on date tag anniversaries to target users on birthdays, app anniversaries, and other recurring dates. --- **Anniversary segments** allow you to target users whose significant dates—like birthdays, first installs, or subscription start dates—occur on the same day each year. This enables you to create personalized, recurring campaigns that celebrate important milestones with your users. ## How to create an anniversary segment To create an anniversary segment: 1. Go to the **Segments** section and click **Create Segment** to open the segment builder. 2. Click **Add filter by** → **Tag** and select the date tag relevant to your anniversary (for example, `First Install` or `birthday`). > **First Install** is a default tag. If you need a custom tag like `birthday`, create one before creating the segment. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags). 3. In the time frame dropdown, navigate to the **ANNIVERSARY** section and select one of the anniversary operators: * **is today**: Users whose anniversary date matches today’s date * **is in N days**: Users whose anniversary is in N days (enter the number of days) * **is N days past**: Users whose anniversary is N days past (enter the number of days) Tip Configuring a segment to include users a certain number of days before or after the anniversary date allows for pre- or post-anniversary engagements. ![Anniversary segment configuration showing date conditions and options](https://docs.pushwoosh.com/segmentation-segments-anniversary-1.webp) 4. Add more filters as needed to narrow down your audience further. 5. Optionally, enable **Include all of a user’s devices** to target a user across all their devices, not just the one that matched the segment condition. This ensures they receive your messages on every platform they use. ![Include all of a user's devices checkbox for cross-platform targeting](https://docs.pushwoosh.com/segmentation-segments-16.webp) ## Anniversary segment examples Below are practical scenarios showing how to use anniversary segments for targeted engagement. ##### Birthday celebrations **Goal:** Send birthday greetings and special offers to users on their birthday. **How to set up:** * **Tag type:** Date * **Tag:** `Birthday` * **Operator:** is today **Use case:** Send personalized birthday messages with discount codes or special perks to celebrate users on their special day. ##### First install anniversary **Goal:** Re-engage users on the anniversary of their first app install. **How to set up:** * **Tag type:** Date * **Tag:** `First Install` * **Operator:** is today **Use case:** Celebrate app usage milestones by sending thank-you messages or exclusive rewards to loyal users. ##### Pre-anniversary reminders **Goal:** Send a reminder a few days before an important date. **How to set up:** * **Tag type:** Date * **Tag:** `SubscriptionRenewalDate` * **Operator:** is in N days * **Value:** `3` **Use case:** Notify users about upcoming subscription renewals or anniversaries, offering early renewal discounts or special preparation offers. ##### Post-anniversary follow-up **Goal:** Follow up after a significant date has passed. **How to set up:** * **Tag type:** Date * **Tag:** `Birthday` * **Operator:** is N days past * **Value:** `2` **Use case:** Send thank-you messages after a birthday celebration or ask for feedback on special offers redeemed. ## Tips for anniversary segments * Once you’ve created an anniversary segment, set up a [recurring customer journey](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-anniversary-campaigns) to automate anniversary campaigns. * Use the “is in N days” condition to send pre-anniversary reminders or prepare users for upcoming events. * Combine anniversary segments with other filters like [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) (e.g., subscription tier, location) for more personalized messaging. * Create [custom tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#custom-tags) for important business-specific dates like membership start dates or first purchase anniversaries. --- # Build segments with AI **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai/ Use natural language and AI to quickly generate audience segments. Describe your ideal audience, and let the system build segment conditions automatically. --- You can now create audience segments faster using AI-powered natural language input. Instead of manually setting conditions, simply describe your target audience. The system will generate a matching segment for you. Important AI-generated segments may not be fully accurate. Always review and validate the segment conditions before using them in a live campaign. To create a segment: 1. Go to **Segments**, then click the chevron next to **Create Segment**. 2. From the dropdown, select **Craft Segment with AI**. 3. In the prompt window that appears, enter a natural language description of the segment you want to create. For example: *“Users who added items to their cart but didn’t complete the purchase in the last 7 days, and are located in the US or Canada.”* ![AI segment creation interface showing natural language prompt input](https://docs.pushwoosh.com/segmentation-segments-14.webp) 4. Click **Generate**. The AI will interpret your prompt and create a segment based on your criteria. ![AI-generated segment conditions based on natural language input](https://docs.pushwoosh.com/segmentation-segments-15.webp) --- # Build segments by events **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/ Create user segments based on app events using frequency, time frame, and event attribute conditions to target users by behavior. --- **Event-based segmentation** allows you to target users based on their actual behavior and actions within your app or website. This creates highly personalized and effective campaigns by focusing on user engagement patterns. ## Build segments by events To build a segment by events: 1. Hit **Event** in the **Add filter by** section. ![Interface showing the Add filter by section with Event option highlighted](https://docs.pushwoosh.com/segmentation-segments-by-events-1.webp) 2. Select an event from the drop-down list. ![Drop-down list displaying available events for segment filtering](https://docs.pushwoosh.com/segmentation-segments-by-events-2.webp) 3. Indicate conditions for the selected event: the number of times it happened and a time frame. > When specifying a time frame for your segment, you can choose from two categories: **Relative dates** and **Specific dates**. ##### Relative dates Use these options for dynamically updating time windows based on the current date: | | | | ------------------------------ | ------------------------------------------------------------------------------------------- | | **during the last N days** | Includes events that occurred within the past N days | | **exactly N days ago** | Targets users who triggered the event exactly N days ago | | **N or more days ago** | Includes users who triggered the event at least N days ago | | **between N and M days ago** | Defines a range of days before today | | **between N and M days ahead** | Defines a range from N to M days from today, where 0 means today. Both N and M are included | ![Example of configuring segment filters by event occurrence and relative date criteria](https://docs.pushwoosh.com/segmentation-segments-21.webp) ##### Specific dates Use these options to define fixed calendar-based filters: | | | | -------------------------------- | ----------------------------------------------------------------------------- | | **on a specific date** | Includes users who triggered the event on a selected calendar date | | **on or after a specific date** | Includes users who triggered the event starting from the selected date onward | | **on or before a specific date** | Includes users who triggered the event up to and including the selected date | | **between dates** | Includes users who triggered the event between two specific calendar dates | ![Interface for segmenting users by event occurrence and specifying the event time frame criteria](https://docs.pushwoosh.com/segmentation-segments-20.webp) 4. (Optional) Click **+ Attribute** to narrow the condition down to a specific event attribute. Select the attribute from the dropdown, choose an operator, and specify a value. Start typing to search suggested values based on previously recorded data. Tip For custom events, add attributes when you [create the event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/#creating-custom-events). Attribute types are listed in [Understanding events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#2-set-attributes). Default events already include their attributes. See [Default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/). For the [**PW_EmailSubscribed**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailsubscribed) / [**PW_EmailUnsubscribed**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailunsubscribed) events, the **categories** attribute value dropdown shows your account’s actual email categories instead of raw recorded values, if the [preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) is enabled. ![Event attribute condition showing attribute name, operator, and value with autocomplete suggestions](https://docs.pushwoosh.com/segmentation-segments-by-events-4.webp) See [event attribute operators by type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#event-attribute-operators-by-type) for the full list of available operators. 5. (Optional) Click **+ Sum** when you need a total across matching events (for example, total spend), not a limit on a single event. Choose an **integer** or **decimal (price)** attribute, an operator, and a threshold. **+ Sum** appears only when the event has at least one integer or decimal (price) attribute. See [operators for sums](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#operators-for-sums). Example: `Purchase` at least once during the last 90 days, with sum of `amount` greater or equals `100`, targets users who spent 100 or more in that window, in one purchase or several. ![Purchase exactly or more 1 time in the last 90 days, with highlighted sum of amount greater or equals 100](https://docs.pushwoosh.com/segmentation-segments-by-events-5.webp) Attribute filters on the same event still apply: the sum uses only events that match them (for example, total `amount` where `category is "shoes"`). You can add more than one sum, on the same or different attributes. A user must pass every sum you add. How are sums calculated? * **Which events count:** Only events that already match this condition’s frequency, time frame, and attribute filters are included in the sum. * **Missing values:** If a matching event has no value for the summed attribute, it counts as `0`. * **Refunds:** Negative values subtract. Track a refund with the same event and a negative amount so the total reflects net spend. * **No matching events:** If the frequency is not **never**, users with zero matching events in the time frame do not pass the sum. For example, sum of `amount` less or equals `0` does not include users who never purchased. To target non-buyers, set the event to **never** without a sum. * **Never with a sum:** If the frequency is **never**, the segment includes every user except those whose matching events meet the sum threshold. For example, `Purchase` **never** during the last 90 days, with sum of `amount` greater or equals `100`, includes users who spent under 100 in that period and users with no purchases. * **Data retention:** By default, Pushwoosh stores event data for one year, so a sum cannot cover a longer period or show lifetime spending. 6. If you set several events as a segment condition, select an operator you’d like to use (AND/OR). [](https://docs.pushwoosh.com/segmentation-segments-5.webm "Setting segment filter by Events showing event selection and time frame conditions") 7. Below the filter groups, enable **Include all of a user’s devices** to target a user across all their devices, not just the one that matched the segment condition. This ensures they receive your messages on every platform they use. If any of a user’s devices matches the conditions, all their devices are added to the segment. If the checkbox is left unchecked, only matching devices are included. This same checkbox appears, disabled, on a segment’s read-only detail page — it reflects the setting the segment was created with and can’t be changed there. ![Include all of a user's devices checkbox for cross-platform targeting](https://docs.pushwoosh.com/segmentation-segments-16.webp) ## Event attribute operators by type The available operators depend on the type of the event attribute you’re filtering by. Select a type below to see its operators. * String | Operator | Description | Example | | --------------- | ----------------------------------------------------------- | --------------------------------------- | | **is** | Attribute value equals the specified value | `button_name is "checkout"` | | **is not** | Attribute value does not equal the specified value | `url is not "/cart"` | | **starts with** | Attribute value begins with the specified string | `url starts with "https://example.com"` | | **ends with** | Attribute value ends with the specified string | `url ends with ".html"` | | **contains** | Attribute value contains the specified string | `url contains "/products/"` | | **in** | Attribute value matches any value in a provided list | `button_name in ["submit", "cancel"]` | | **not in** | Attribute value does not match any value in a provided list | `category not in ["spam", "test"]` | | **not set** | No value was recorded for the attribute | `coupon_code not set` | | **any** | Attribute has any recorded value | `referrer any` | * Integer | Operator | Description | Example | | --------------------- | ---------------------------------------------------------------- | ------------------------------------- | | **is** | Attribute value equals the specified number | `level_number is 5` | | **is not** | Attribute value does not equal the specified number | `score is not 0` | | **greater or equals** | Attribute value is greater than or equal to the specified number | `points_earned greater or equals 100` | | **less or equals** | Attribute value is less than or equal to the specified number | `attempts less or equals 3` | | **between** | Attribute value is within the specified range (inclusive) | `level_number between 10 and 20` | | **in** | Attribute value matches any value in the list | `level_number in [5, 10, 15]` | | **not in** | Attribute value does not match any value in the list | `attempts not in [0, 1]` | | **not set** | No value was recorded for the attribute | `score not set` | | **any** | Attribute has any recorded value | `level_number any` | * Boolean | Operator | Description | Example | | ------------ | ------------------------------------------------ | ------------------- | | **is true** | Attribute value is true | `enabled is true` | | **is false** | Attribute value is false | `is_trial is false` | | **not set** | No value was recorded for the attribute | `enabled not set` | | **any** | Attribute has any recorded value (true or false) | `is_trial any` | * Decimal (price) Use this type for monetary values with decimals. In the Control Panel it appears as **decimal (price)** when you create or edit an event attribute. | Operator | Description | Example | | --------------------- | ------------------------------------------------------------------ | ------------------------------------- | | **is** | Attribute value equals the specified price | `total_price is 9.99` | | **is not** | Attribute value does not equal the specified price | `total_price is not 0` | | **greater or equals** | Attribute value is greater than or equal to a price | `total_price greater or equals 50.00` | | **less or equals** | Attribute value is less than or equal to a price | `total_price less or equals 100.00` | | **between** | Attribute value falls within the specified price range (inclusive) | `total_price between 10.00 and 50.00` | | **in** | Attribute value matches any value in a list | `total_price in [9.99, 14.99, 19.99]` | | **not in** | Attribute value does not match any value in a list | `total_price not in [0, 5]` | | **not set** | No value was recorded for the attribute | `total_price not set` | | **any** | Attribute has any recorded value | `total_price any` | * List List-type attributes can hold multiple values at once, such as product IDs added to a cart. | Operator | Description | Example | | ----------- | ------------------------------------------------------- | ----------------------------------------- | | **in** | Attribute contains at least one of the specified values | `product_ids in ["prod-123", "prod-456"]` | | **not in** | Attribute does not contain any of the specified values | `product_ids not in ["prod-789"]` | | **not set** | No value was recorded for the attribute | `product_ids not set` | | **any** | Attribute has any recorded value | `product_ids any` | * Date Date-type attributes support the same [relative](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#relative-dates) and [specific date](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#specific-dates) comparisons used for the event’s own time frame. ## Operators for sums A sum condition compares the total of an attribute across the matched events, so it supports fewer operators than an attribute condition. | Operator | Description | Example | | --------------------- | ---------------------------------------------------------- | ----------------------------------- | | **is** | The total equals the specified number | `total_price is 100` | | **is not** | The total does not equal the specified number | `total_price is not 0` | | **greater or equals** | The total is greater than or equal to the specified number | `total_price greater or equals 100` | | **less or equals** | The total is less than or equal to the specified number | `total_price less or equals 500` | | **between** | The total is within the specified range (inclusive) | `total_price between 100 and 500` | Tip Sums keep the sign of each value. To account for refunds, track them with the same event and a negative amount, so that the sum reflects the net amount spent. ## Event-based segmentation examples Below are several practical scenarios illustrating how you can use event filters to create meaningful audience segments. ##### Frequent purchasers **Goal:** Target highly engaged users who have made multiple purchases recently. **How to set up:** * **Event:** `Purchase_made` * **Condition:** exactly or more * **Times:** `3` * **Time frame:** during the last N days * **Time frame value:** `30` **Use case:** Send special offers or loyalty rewards to users who demonstrate frequent buying activity. ##### High-value single purchase **Goal:** Target users who made a large purchase, regardless of how often they buy. **How to set up:** * **Event:** `Purchase_made` * **Condition:** exactly or more * **Times:** `1` * **Time frame:** during the last N days * **Time frame value:** `30` * **Attribute:** `total_price` * **Operator:** greater or equals * **Attribute value:** `50` **Use case:** Invite big spenders to a loyalty or VIP program right after a qualifying purchase. ##### Total spend over a period **Goal:** Target users whose combined purchases reach a spending threshold, even if no single purchase does. **How to set up:** * **Event:** `Purchase_made` * **Condition:** exactly or more * **Times:** `1` * **Time frame:** during the last N days * **Time frame value:** `90` * **Sum:** `total_price` * **Operator:** greater or equals * **Sum value:** `200` **Use case:** Move customers into a higher loyalty tier once their spending over the last 90 days reaches the threshold. ##### Refunds exceed purchases **Goal:** Target users who got back more than they spent over a period. This works when refunds are tracked with the same event and a negative amount. **How to set up:** * **Event:** `Purchase_made` * **Condition:** exactly or more * **Times:** `1` * **Time frame:** during the last N days * **Time frame value:** `90` * **Sum:** `total_price` * **Operator:** less or equals * **Sum value:** `-1` **Use case:** Route these users to a support or win-back flow instead of promotional campaigns. ##### Viewed products but no purchase **Goal:** Identify users who have shown interest but have not converted. **How to set up:** * **Event 1:** `Product_Viewed` * **Condition:** exactly or more * **Times:** `1` * **Time frame:** during the last N days * **Time frame value:** `7` * **Event 2:** `Purchase_made` * **Condition:** never * **Time frame:** during the last N days * **Time frame value:** `7` * **Operator:** AND **Use case:** Send reminders, special offers, or personalized messages to users who browsed products but haven’t made a purchase yet. ##### Users engaged during a campaign **Goal:** Target users who participated during a specific promotional period. **How to set up:** * **Event:** `Promotion_Click` * **Condition:** exactly or more * **Times:** `1` * **Time frame:** between dates * **Time frame value:** `"2024-01-01"` and `"2024-01-31"` **Use case:** Follow up with users who interacted with a past campaign to promote a related offer or gather feedback. ##### High-value actions over time **Goal:** Identify users who performed a valuable event a certain number of times within a custom date range. **How to set up:** * **Event:** `Subscription_Renewal` * **Condition:** exactly or more * **Times:** `2` * **Time frame:** between dates * **Time frame value:** `"2024-01-01"` and `"2024-12-31"` **Use case:** Offer special recognition or advanced features to loyal subscribers who regularly renew their subscriptions. ## Tips for event-based segmentation * Use [relative dates](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#relative-dates) for ongoing campaigns that should stay updated with current user activity. * Narrow an event condition down with an [event attribute filter](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#event-attribute-operators-by-type) (e.g., purchase amount, product category) instead of relying on event occurrence alone. * Combine event filters with other criteria such as [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) (e.g., user demographics, subscription status) or [existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) for more precise targeting. For example, you can filter users based on both event occurrence and user properties stored in tags to tailor your messaging even further. * Periodically review and update your segment filters to adapt to changing user behaviors or business goals. --- # Build segments by existing segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/ Create segments by combining existing segments to reuse audience logic and build layered targeting strategies. --- **Segment-based filtering** allows you to include users who already belong to one or more existing segments. This lets you reuse previously defined logic instead of recreating complex conditions from scratch, making it easier to build layered audience targeting. ## How to build segments by existing segments To build a segment using existing segments: 1. When creating a new segment, click **Add filter by** → **Segment**. ![Example of using the Segment filter to include users from existing segments](https://docs.pushwoosh.com/segmentation-segments-22.webp) 2. Select one or more existing segments to include in your new segment. 3. If you add multiple segments, choose how they should be combined using a logic operator: * Select **AND** to include users who belong to all selected segments * Select **OR** to include users who belong to any of the selected segments ![Interface illustrating selection of multiple existing segments and applying logic operators (AND/OR)](https://docs.pushwoosh.com/segmentation-segments-26.webp) Segment nesting restrictions When using the **Segment** filter, keep the following restrictions in mind: 1. A segment cannot include itself, either directly or through another segment. Circular or recursive segment references are not supported. 2. You can create up to five levels of nested segments. For example: * **Segment A** includes **Segment aaa** * **Segment aaa** includes **Segment bbb** * **Segment bbb** includes **Segment ccc** and so on, up to five levels of nesting. ## Include all of a user’s devices Below the filter groups, enable **Include all of a user’s devices** to target a user across all their devices, not just the one that matched the segment condition. This ensures they receive your messages on every platform they use. If any of a user’s devices matches the conditions, all their devices are added to the segment. If the checkbox is left unchecked, only matching devices are included. This same checkbox appears, disabled, on a segment’s read-only detail page — it reflects the setting the segment was created with and can’t be changed there. ![Include all of a user's devices checkbox for cross-platform targeting](https://docs.pushwoosh.com/segmentation-segments-16.webp) ## Use cases for segment-based filtering Below are practical scenarios showing how to use existing segments to create more targeted audiences. ##### Premium active users **Goal:** Target only premium subscribers who are recently active. **How to set up:** * **Segment filter:** Select `Active Users` (users who opened the app in the last 7 days) * **Tag filter:** `subscription_type_premium` is true * **Operator:** AND **Use case:** Send exclusive offers or premium feature announcements to engaged, paying users. ![Example of using Segment and Tag filters together to narrow down your audience](https://docs.pushwoosh.com/segmentation-segments-23.webp) ##### Multi-segment engagement **Goal:** Target users who belong to either high-value customers or recent purchasers. **How to set up:** * **Segment 1:** Select `High-Value Customers` * **Segment 2:** Select `Recent Purchasers` * **Operator:** OR **Use case:** Run promotional campaigns targeting users with demonstrated purchase behavior from multiple angles. ## Tips for segment-based filtering * Reuse well-defined segments to maintain consistency across campaigns and reduce setup time. * Combine segment filters with [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) or [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) for more precise targeting. * Keep track of segment nesting levels to avoid hitting the five-level limit. * Regularly review and update base segments to ensure derived segments remain accurate. --- # Build segments by geolocation **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo/ Create segments with the Geo filter to target users near a selected point within a defined radius for location-based campaigns. --- Use the **Geo** filter to create a segment of users who are near a selected location. This is useful for location-based campaigns, local offers, event reminders, and messages tied to a physical area. ## How location is determined The **Geo** filter uses the device coordinates available in Pushwoosh to check whether the device is within the radius you set. These coordinates can come from: * Coordinates sent through [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) * The device IP address, with city-level accuracy, when coordinates are not available ## How to build a segment by geolocation To create a geolocation-based segment: 1. Go to **Audience** → **Segments**. 2. Click **Create Segment** to open the segment builder. 3. In the segment builder, click **Geo** in **Add filter by**. ![Create segment screen with Geo selected in Add filter by before setting location and radius](https://docs.pushwoosh.com/segmentation-segments-by-geo-2.webp) 4. Set the location and radius. The Geo filter lets you define a circular area around a point on the map. From the screen, you can: * Use **Find a place…** to search for a location. * Enter latitude and longitude values. * Set the radius in meters in the **within** field. * Adjust the selected area on the map. ![Create segment screen with Geo filter configured showing map, coordinates, radius, and place search](https://docs.pushwoosh.com/segmentation-segments-by-geo-1.webp) 5. Add more filters if needed. 6. To target a user across all their devices when one device matches the Geo condition, use **Include all of a user’s devices**. [Learn how this option works](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#include-all-user-devices). 7. Click **Save**. Tip You can combine **Geo** with **Tag**, **Event**, **Segment**, or **User ID** filters in the same segment. For more complex logic, see [Build segments with compound filters](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters/). --- # Import a segment **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/ Create segments by uploading a CSV file containing User IDs or Hardware IDs without specifying filtering conditions. --- **CSV import** allows you to create a segment by uploading a file containing User IDs or Hardware IDs (HWIDs) without needing to specify filtering conditions. This is useful when you have a predefined list of users from external sources. Note This imports a list of users or devices already registered in Pushwoosh. To register new devices and push tokens from another provider’s export (OneSignal, Amazon Pinpoint), see [Import push subscribers](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/import-push-subscribers/) instead. ## How to import a segment To import a segment from a CSV file: 1. Go to **Audience** → **Segments**, click the chevron next to **Create Segment**, and select **Import segment**. ![Import segment modal showing Upload CSV step with file upload area](https://docs.pushwoosh.com/segmentation-segments-by-import-2.webp) 2. Upload your CSV file. Ensure your CSV file meets the following requirements: * Contains [User IDs](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) or [Hardware IDs (HWIDs)](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#hwid) registered for your project. If the CSV file does not contain any User ID or HWID, it won’t create any segment. * File size is less than 100 MB * If User IDs or HWIDs are not registered for your project, they will be skipped during import. ![Upload CSV step completed with option to upload another CSV file](https://docs.pushwoosh.com/segmentation-segments-by-import-3.webp) 3. In the **Preparing for import** step: * Enter a **Segment name** (e.g. vip_clients). * Under **CSV column with identifiers**, select the column that contains User IDs or HWIDs (the identifier values registered in your project). The values in that column must be the actual User IDs or HWIDs. * Under **System identifier**, choose **User ID** or **Hardware ID** (HWID) to map that column to Pushwoosh. * Click **Import**. ![Import segment modal at Preparing for import step with Segment name, CSV column with identifiers, and System identifier dropdowns](https://docs.pushwoosh.com/segmentation-segments-by-import-1.webp) Tip To replace the file before continuing, click **Upload another CSV**. 4. Wait for the import to complete. Processing the CSV file might take some time. You can leave the page while the import completes. ![Segments list showing user counts, refresh times, and segment conditions](https://docs.pushwoosh.com/segmentation-segments-10.webp) After the import is finished, the segment will appear in the [Segments list](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#segments-list). ## CSV import tag After import, Pushwoosh tags all users or devices from the file with a “CSV import” tag (value is the import timestamp). ![CSV import tag shown in segment definition (Seglang) in Segments list](https://docs.pushwoosh.com/segmentation-segments-9.webp) You see this in the [Segments list](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#segments-list) under the segment name and in each user’s profile in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). ## View imported segments In the [Segments list](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#segments-list), you’ll see the segment name, the segment definition, the number of users, the last time the segment size was refreshed, and the modified date. --- # Build segments by tags **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/ Create segments using tag filters to target users based on assigned attributes, preferences, and profile data. --- Tags are custom attributes you can assign to users to store additional data about their preferences, behavior, or profile information. Build segments by using tag filters to target users based on specific attributes or data assigned to their devices and user IDs. ## How to create a tag-based segment To create a tag-based segment, do the following: 1. In the **Create Segment** page, click **Add filter by**. ![Add filter by dropdown menu showing tag selection option in segment creation interface](https://docs.pushwoosh.com/segmentation-segments-by-tag-1.webp) 2. Select a tag from the **Tag** dropdown list. Start typing to search for specific tags if you have many. Tip Tags must be created first before you can use them in segments. Learn how to create and manage tags in the [User Data (Tags) documentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/). 3. Click **Add value** to specify the tag condition and select a value from the dropdown or start typing to search. [](https://docs.pushwoosh.com/segmentation-segments-2.webm "Setting segment filter by Tags showing tag selection and value assignment") 4. Choose the appropriate operator for your condition. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#tag-operators-by-type) ## Set operators for multiple conditions ### Logical operators When you add multiple tag conditions to your segment, you can use logical operators to combine them: * **AND**: Includes users who match **all** specified tag values * **OR**: Includes users who match **any** of the specified tag values Use the same tag multiple times You can include the same tag more than once within a segment to create advanced targeting rules. For example, to find users who have purchased a coat but not gloves, add the **In-app Product** tag with **equals** set to “Coat.” Then, add the same tag again with **does not equal** set to “Gloves.” Use the **AND** operator to target users who satisfy both conditions at the same time. ![Segment filter example showing same tag used twice with different conditions using AND operator](https://docs.pushwoosh.com/segmentation-segments-4.webp) ### Filter groups When you add multiple filters to a segment, they form a **Filter Group**. All filters within the same group use the same logical operator (AND or OR). To change the operator for the entire filter group: 1. Click on the current operator (AND/OR) in the filter group header. 2. Select the new operator from the dropdown. [](https://docs.pushwoosh.com/segmentation-segments-3.webm "Changing Filters operator between AND and OR logic") ## Include all of a user’s devices Below the filter groups, enable **Include all of a user’s devices** to target a user across all their devices, not just the one that matched the segment condition. This ensures they receive your messages on every platform they use. If any of a user’s devices matches the conditions, all their devices are added to the segment. If the checkbox is left unchecked, only matching devices are included. This same checkbox appears, disabled, on a segment’s read-only detail page — it reflects the setting the segment was created with and can’t be changed there. ![Include all of a user's devices checkbox for cross-platform targeting](https://docs.pushwoosh.com/segmentation-segments-16.webp) ## Tag operators by type The available operators depend on the tag type you’re working with. Below are the operators for each tag type. ### String tag operators String tags store text values such as usernames, email addresses, product names, or identifiers. Use them to segment users by categories, preferences, or any custom text-based data. | Operator | Description | Example | | --------------- | ----------------------------------------------------- | --------------------------------------- | | **is** | Tag value equals the specified value | `FavoriteColor is "Blue"` | | **is not** | Tag value does not equal the specified value | `Country is not "Canada"` | | **starts with** | Tag value begins with the specified string | `ProductName starts with "Pro"` | | **ends with** | Tag value ends with the specified string | `Email ends with "@gmail.com"` | | **contains** | Tag value contains the specified string | `Description contains "premium"` | | **in** | Tag value matches any value in a provided list | `Category in ["Sports", "News"]` | | **not in** | Tag value does not match any value in a provided list | `Status not in ["Banned", "Suspended"]` | | **not set** | Tag has no value assigned | `Nickname not set` | | **any** | Tag has any value | `Username any` | ### Integer tag operators Integer tags store whole numbers like age, level, score, or purchase count. Perfect for demographic targeting and behavior-based segmentation using numeric comparisons. | Operator | Description | Example | | --------------------- | ---------------------------------------------------------- | -------------------------------- | | **is** | Tag value equals the specified number | `Age is 30` | | **is not** | Tag value does not equal the specified number | `Level is not 1` | | **greater or equals** | Tag value is greater than or equal to the specified number | `Score greater or equals 100` | | **less or equals** | Tag value is less than or equal to the specified number | `PurchaseCount less or equals 5` | | **between** | Tag value is within the specified range (inclusive) | `Age between 18 and 65` | | **in** | Tag value matches any value in the list | `Level in [5, 10, 15]` | | **not in** | Tag value does not match any value in the list | `Streak not in [0, 1]` | | **not set** | Tag has no value assigned | `Score not set` | | **any** | Tag has any assigned value | `Level any` | ### List tag operators List tags allow multiple values per user simultaneously, ideal for preferences, interests, or subscriptions where users can select multiple options (e.g., news categories, product interests). | Operator | Description | Example | | ----------- | ------------------------------------------------- | ------------------------------------------ | | **in** | Tag contains at least one of the specified values | `Interests in ["Sports", "Technology"]` | | **not in** | Tag does not contain any of the specified values | `Categories not in ["Spam", "Promotions"]` | | **not set** | Tag has no value assigned | `Preferences not set` | | **any** | Tag has any assigned value | `Subscriptions any` | ### Boolean tag operators Boolean tags store only true or false values. Use them for binary states like email verification, subscription status, or feature flags. | Operator | Description | Example | | ------------ | ------------------------------------------ | ----------------------- | | **is true** | Tag value is true | `EmailVerified is true` | | **is false** | Tag value is false | `PushEnabled is false` | | **not set** | Tag was never assigned a value | `SMSConsent not set` | | **any** | Tag has any assigned value (true or false) | `AccountActive any` | ### Date tag operators Date tags store calendar dates. Use them to segment users based on signup dates, last activity, birthdays, or any time-sensitive events with flexible relative and absolute date comparisons. ##### Relative dates These operators compare dates relative to today, allowing dynamic time-based segmentation that automatically updates each day. | Operator | Description | Example | | ------------------------------ | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | | **during the last N days** | Date is within the last N days | `LastOpen during the last 30 days` | | **exactly N days ago** | Date matches exactly N days ago | `LastLogin exactly 7 days ago` | | **N or more days ago** | Date is older than or equal to N days | `LastPurchase 90 or more days ago` | | **between N and M days ago** | Date is between two day offsets | `Signup between 7 and 30 days ago` | | **between N and M days ahead** | Date falls in the next N to M days, where 0 means today, 1 means tomorrow, and so on. Both N and M are included | `RenewalDate between 0 and 30 days ahead` | ##### Anniversary These operators match recurring calendar dates (same month and day each year), ignoring the year. Perfect for birthdays and anniversary campaigns. | Operator | Description | Example | | ------------------ | -------------------------- | ------------------------- | | **is today** | Anniversary occurs today | `Birthday is today` | | **is in N days** | Anniversary is in N days | `Birthday is in 3 days` | | **is N days past** | Anniversary was N days ago | `Birthday is 5 days past` | ##### Specific dates These operators compare against fixed calendar dates. Use them for one-time campaigns or historical date analysis. | Operator | Description | Example | | -------------------------------- | --------------------------------------- | ------------------------------------------------ | | **on a specific date** | Matches exact date | `LastLogin on "2023-01-15"` | | **on or after a specific date** | Date is on or after the specified date | `LastPurchase on or after "2023-01-01"` | | **on or before a specific date** | Date is on or before the specified date | `AccountCreated on or before "2022-12-31"` | | **between dates** | Between two calendar dates | `LastOpen between "2022-02-28" and "2022-03-02"` | ##### Existence These operators check only whether a date tag has been assigned, without evaluating the actual date value. | Operator | Description | Example | | ----------- | ----------------- | ---------------------- | | **not set** | Tag has no value | `TrialEndDate not set` | | **any** | Tag has any value | `LastLogin any` | ### Price tag operators Price tags store monetary values with decimal precision in currency format (X.XX). Ideal for segmenting users by purchase amounts, subscription tiers, cart values, or spending behavior for revenue-focused campaigns. | Operator | Description | Example | | --------------------- | -------------------------------------------------- | ------------------------------------------- | | **is** | Price equals the specified value | `InAppPurchase is 9.99` | | **is not** | Price does not equal the specified value | `LastPurchase is not 0` | | **greater or equals** | Price is greater than or equal to a value | `TotalSpent greater or equals 100` | | **less or equals** | Price is less than or equal to a value | `CartValue less or equals 50` | | **between** | Price falls within the specified range (inclusive) | `OrderValue between 25 and 100` | | **in** | Price matches any value in a list | `SubscriptionPrice in [9.99, 14.99, 19.99]` | | **not in** | Price does not match any value in a list | `RefundAmount not in [0, 5]` | | **not set** | Tag has no value assigned | `LastPurchaseAmount not set` | | **any** | Tag has any assigned value | `PurchaseHistory any` | ### Version tag operators Version tags store version numbers in w.x.y.z format (Major.Minor.Patch.Build) for app, OS, or SDK versions. Perfect for targeting users on specific versions for updates, compatibility messaging, or feature rollouts. | Operator | Description | Example | | --------------------- | --------------------------------------------------------- | ------------------------------------------- | | **is** | Version equals the specified value | `AppVersion is "2.5.0"` | | **is not** | Version does not equal the specified value | `AppVersion is not "1.0.0"` | | **greater or equals** | Version is greater than or equal to the specified version | `AppVersion greater or equals "2.0.0"` | | **less or equals** | Version is less than or equal to the specified version | `AppVersion less or equals "3.0.0"` | | **between** | Version falls within a version range | `AppVersion between "2.0.0" and "3.0.0"` | | **in** | Version equals any of the listed values | `AppVersion in ["2.1.0", "2.2.0", "2.3.0"]` | | **not in** | Version does not equal any of the listed values | `OSVersion not in ["10.0", "11.0"]` | | **not set** | Tag has no version assigned | `SDKVersion not set` | | **any** | Tag has any version assigned | `AppVersion any` | ## Tag-based segmentation examples Below are practical scenarios showing how to use tag filters to create targeted audience segments. ##### Premium subscribers **Goal:** Target users with an active premium subscription. **How to set up:** * **Tag type:** String * **Tag:** `subscription_type` * **Operator:** is * **Value:** `premium` **Use case:** Send exclusive content, early access to features, or special premium-only offers to engaged paying users. ##### Age-targeted campaigns **Goal:** Target users within a specific age range. **How to set up:** * **Tag type:** Integer * **Tag:** `Age` * **Operator:** greater or equals * **Value:** `25` AND * **Tag:** `Age` * **Operator:** less or equals * **Value:** `35` **Use case:** Deliver age-appropriate content, products, or services tailored to specific demographics. ##### Users by interests **Goal:** Target users who have selected specific interests or categories. **How to set up:** * **Tag type:** List * **Tag:** `Interests` * **Operator:** in * **Value:** `["Sports", "Technology"]` **Use case:** Send personalized content based on user interests, recommend relevant products, or create interest-based campaigns. ##### Email verified users **Goal:** Target users who have verified their email addresses. **How to set up:** * **Tag type:** Boolean * **Tag:** `EmailVerified` * **Operator:** is true * **Value:** `true` **Use case:** Send messages only to verified users, improve deliverability for important communications. ##### Recent signups **Goal:** Target users who signed up within the last 30 days. **How to set up:** * **Tag type:** Date * **Tag:** `SignupDate` * **Operator:** during the last N days * **Value:** `30` **Use case:** Onboard new users with welcome messages, tutorials, or special offers for first-time customers. ##### High-value purchasers **Goal:** Target users who have made purchases above a certain amount. **How to set up:** * **Tag type:** Price * **Tag:** `TotalSpent` * **Operator:** greater or equals * **Value:** `100.00` **Use case:** Offer exclusive deals to high spenders, invite to VIP programs, or send premium product recommendations. ##### App version targeting **Goal:** Target users on a specific app version for update campaigns. **How to set up:** * **Tag type:** Version * **Tag:** `AppVersion` * **Operator:** less or equals * **Value:** `2.0.0` **Use case:** Notify users about important updates, promote new features available in newer versions, or address compatibility issues. ## Tips for tag-based segmentation * Create clear and consistent tag naming conventions across your project to make segments easier to build and maintain. * Use multiple tag conditions with AND/OR operators to create precise audience definitions. * Combine tag filters with [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) or [existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) for even more targeted campaigns. * Regularly review and update tag values to ensure your segments remain accurate as user data changes. --- # Build segments by User ID **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/ Create segments with the User ID filter to target known users or devices by their User ID, HWID, email, phone, or push token. --- Use the **User ID** filter to create a segment of known users by their Pushwoosh User ID, or switch it to **HWID**, **Email**, **Phone**, or **Token** to match by that device identifier instead. This works best when you already know which users or devices you want to include and those identifiers are registered in your project. ## How to build a segment by User ID To create a User ID-based segment: 1. Go to **Audience** → **Segments**. 2. Click **Create Segment** to open the segment builder. 3. In the segment builder, click **User ID** in **Add filter by**. ![Create segment screen with User ID selected in Add filter by before entering user identifiers](https://docs.pushwoosh.com/segmentation-segments-by-user-id-2.webp) 4. Choose which identifier to filter by: **User ID**, **HWID**, **Email**, **Phone**, or **Token**. ![User ID condition field dropdown showing User ID, HWID, Email, Phone, and Token all enabled with Token selected](https://docs.pushwoosh.com/segmentation-segments-by-user-id-4.webp) 5. Choose the operator for the condition. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/#operators) ![User ID condition operator dropdown showing in, not in, starts with, contains, and ends with](https://docs.pushwoosh.com/segmentation-segments-by-user-id-3.webp) 6. Add the values that should be included in the segment: User IDs, HWIDs, emails, phone numbers, or push tokens, depending on the field you chose in step 4. **in** and **not in** take a list of values. **starts with**, **contains**, and **ends with** take a single value. Enter phone numbers in [**E.164**](https://en.wikipedia.org/wiki/e.164) format, for example `+15551234567`. Pushwoosh stores them this way, and **starts with** only matches from the very first character. Note Use identifiers that already exist in your Pushwoosh project. If you need to upload a file with User IDs or Hardware IDs, use [Import a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/). ![Create segment screen with User ID filter open showing the field to add Pushwoosh User IDs](https://docs.pushwoosh.com/segmentation-segments-by-user-id-1.webp) 7. Add more filters if needed. 8. To target a user across all their devices when the condition matches, use **Include all of a user’s devices**. [Learn how this option works](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#include-all-user-devices). 9. Click **Save**. Tip You can combine **User ID** with **Tag**, **Event**, **Segment**, or **Geo** filters in the same segment to narrow a known user list by behavior, attributes, existing segment membership, or location. For more complex logic, see [Build segments with compound filters](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters/). ## Operators Each field takes the same five operators, comparing the values you enter against that field’s value on each user or device. | Operator | Description | Example | | --------------- | ------------------------------------ | ------------------------------ | | **in** | Matches any value in the list | `UserID in ["user1", "user2"]` | | **not in** | Does not match any value in the list | `HWID not in ["a1b2c3d4e5"]` | | **starts with** | Begins with the specified string | `Phone starts with "+1555"` | | **contains** | Contains the specified string | `Email contains "example.com"` | | **ends with** | Ends with the specified string | `Token ends with "9a2e2f"` | ### Multi-device matching For **HWID**, **Email**, **Phone**, and **Token**, operators evaluate each device individually. A user matches if any one of their devices satisfies the condition. **not in** matches any user with at least one device whose value is not in the list. If a user has several devices, one device outside the list is enough to match, even if another device of that user is on the list. Turning on **Include all of a user’s devices** adds every device of a matching user to the segment, including devices whose values are on the excluded list. ### Token matching **Token** matches the [push token](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#push-token) shown for a device in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). It doesn’t match Firebase Cloud Messaging (FCM) sender keys or web push keys, which aren’t indexed for this filter. Unlike **Email**, **Token** values are case-sensitive. Pushwoosh compares them exactly as entered, without lowercasing. Note Pushwoosh compares **Email** values in lowercase, so casing in what you type doesn’t matter. New email devices are stored in lowercase, but a small share of existing devices still have their address stored with uppercase letters. This filter can’t match those regardless of how you enter the value. ### Legacy segments Segments saved before these operators were introduced carry no operator value and are read as **in**. Nothing changes for existing segments. --- # Build segments with compound filters **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters/ Create advanced segments by combining multiple filter types and groups using logical operators for precise targeting. --- **Compound filtering** allows you to combine multiple filter types and create complex filter groups within a single segment using logical operators. This enables you to build highly targeted audiences by mixing tags, events, and other conditions with precise control over how they interact. You can set both [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) and [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) at the same time, and choose an operator (AND/OR) to define how they work together. **Example** This segment includes all users who bought linen or lyocell trenchcoat in the app and successfully checked out (with any purchase) more than once in the last 30 days. [](https://docs.pushwoosh.com/segmentation-segments-6.webm "Setting segment filter by Tags and Events showing combined conditions") ## Combine multiple filter groups To build more advanced and targeted segments, combine multiple filter groups in one segment using different logic operators: * **AND**: includes users who meet all conditions from both groups * **OR**: includes users who meet at least one of the groups’ conditions * **AND NOT**: includes devices from the first group but excludes devices that meet the second group’s conditions ##### Example using OR This segment includes users who meet either of the following: * Bought a linen trenchcoat at least once in the last 30 days OR * Added mom jeans to their wishlist and added at least 3 items to their cart in the last 30 days ![Complex segment with multiple filter groups showing AND/OR logic](https://docs.pushwoosh.com/segmentation-segments-7.webp) ##### Example using AND NOT This segment targets high-spending users who are not part of the loyalty program: * **Included:** Users in the *High-spending* segment * **Excluded:** Users in the *Loyalty members* segment ![Segment example using AND NOT logic](https://docs.pushwoosh.com/segmentation-segments-24.webp) Note By default, exclusion is evaluated per device, not per user. If a user has several devices and only one of them matches the excluded group, the user’s other devices stay in the segment — and a user-level send, such as a Customer Journey entry, will still reach that user. To exclude the whole user, enable **Include all of a user’s devices** in the segment settings. --- # External segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/ Build a segment from users in your PostgreSQL, MySQL, BigQuery, or SQL Server / Fabric database, or a Google Analytics 4 audience. --- External segments let you target people already stored in your own database, like your CRM or data warehouse, instead of exporting them to a CSV and re-uploading it every time the list changes. For a database, you give Pushwoosh a SQL query that returns the right user IDs, and Pushwoosh keeps rerunning that query to keep the segment up to date. You can also use a Google Analytics 4 audience, which needs no query. Once it’s set up, you can use these users the same way as any other segment. Developer assistance needed You’ll need help from your developer or database administrator to set up the connection. Share this guide with them to get started. Setting up a connection touches your production database. Before you start, have your developer or database administrator: * Prepare a read-only user (or, for BigQuery, a read-only service account) * Confirm the query you plan to use * If the database is behind a firewall, ask Pushwoosh support which IP addresses to allow ## Create an external segment ### Name the segment and choose the database 1. Go to **Audience** → **External Segments**. 2. Click **Create external segment**. ![External Segments page in the Control Panel showing the empty list and the Create external segment button](https://docs.pushwoosh.com/segmentation-external-segments-1.webp) 3. Enter a **Name** for the segment (up to 100 characters). 4. Choose the **Database type**: **PostgreSQL**, **MySQL**, **Google BigQuery**, **Microsoft SQL Server / Fabric**, or **Google Analytics 4 audience**. ![Create external segment form with the Name filled in and Database type set to Microsoft SQL Server / Fabric](https://docs.pushwoosh.com/segmentation-external-segments-4.webp) ### Fill in the connection fields #### PostgreSQL and MySQL For **PostgreSQL** and **MySQL**, fill in: * **Host** and **Port:** the address of your database server. Pushwoosh requires a publicly reachable host and rejects private, internal, or loopback addresses when you check the connection or save the segment. * **Database:** the database name. * **User** and **Password:** credentials for a read-only account. * **SSL mode** (PostgreSQL only, since MySQL connections don’t use TLS) sets how SSL/TLS, the protocol that encrypts the connection, applies to your database connection. Ask your database administrator which mode to use: * **Disable:** no encryption. * **Require:** encrypts the connection, but doesn’t check the database server’s identity. * **Verify CA:** encrypts the connection and checks that the server’s certificate was issued by a trusted certificate authority (CA). * **Verify full:** same as **Verify CA**, and also checks the certificate matches your database’s hostname. The strongest option. * For any mode other than **Disable**, provide the **CA certificate** under **Certificate source** (**Upload files** or **Paste PEM**), plus a **Client certificate** and **Client key** if your database requires them. ![External segment creation form with PostgreSQL connection fields, SSL mode set to Require, and certificate upload fields](https://docs.pushwoosh.com/segmentation-external-segments-2.webp) #### BigQuery For **Google BigQuery**, fill in: * **Project ID:** the GCP project that holds your dataset. * **Service account JSON:** the key for a service account with BigQuery read access. * **Location:** the dataset’s region (for example, `EU`, `US`, or `europe-west3`). Leave it empty to resolve the location automatically from the dataset. ![External segment creation form with BigQuery connection fields: Project ID, Service account JSON, and Location](https://docs.pushwoosh.com/segmentation-external-segments-3.webp) #### SQL Server / Fabric For **Microsoft SQL Server / Fabric**, fill in: * **Host** and **Port:** the address of your database server, or your Fabric SQL analytics endpoint. Pushwoosh requires a publicly reachable host and rejects private, internal, or loopback addresses when you check the connection or save the segment. * **Database:** the database name. * **User** and **Password:** credentials for a read-only account. * **Microsoft Entra tenant ID:** optional. Leave it empty for a SQL Server login. For Microsoft Fabric, whose SQL analytics endpoint has no SQL logins, set it to sign in as a service principal instead. Enter its application (client) ID as User and its client secret as Password. ![External segment creation form with Microsoft SQL Server / Fabric connection fields: Host, Port, Database, User, Password, and Microsoft Entra tenant ID](https://docs.pushwoosh.com/segmentation-external-segments-7.webp) #### Google Analytics 4 audience A **Google Analytics 4 audience** segment takes its users from an audience you built in Google Analytics, so it has no query. Fill in: * **GA4 property ID:** the ID of the Google Analytics property, shown in Google Analytics under **Admin** → **Property details**. * **Service account JSON:** the key for a service account with the **Viewer** role on that property. Grant the role in Google Analytics under **Admin** → **Property access management**, and enable the Google Analytics Data API and Admin API in the service account’s Google Cloud project. * **Audience:** click **Load audiences**, then choose the audience. If the list is empty, create an audience in Google Analytics first. ![Google Analytics 4 audience form with GA4 property ID, Service account JSON, the Purchasers audience, and Check connection](https://docs.pushwoosh.com/segmentation-external-segments-8.webp) For the setup on the Google Analytics side, see [Google Analytics integration](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/#import-a-google-analytics-audience). Then click **Check connection**. When the check succeeds, click **Create**. ### Enter the query For every database type except **Google Analytics 4 audience**: 1. Enter the **Query**. It must return a single column named `user_id`, with one row per user you want in the segment. For example: ```sql SELECT user_id FROM loyalty_members WHERE tier = 'gold' ``` Note The `user_id` values must match the Pushwoosh User IDs you’ve set for your users (the same identifier used by [Build segments by User ID](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id)). A query returning IDs Pushwoosh doesn’t recognize won’t match any devices. 2. Click **Check connection** to confirm Pushwoosh can reach the database (times out after 30 seconds). 3. Click **Check query** to confirm the query runs and returns the right column (also times out after 30 seconds). ![Query field with an example SQL query, the user_id column requirement, and the Check connection and Check query buttons](https://docs.pushwoosh.com/segmentation-external-segments-6.webp) ### Verify and create When both checks succeed, click **Create**. Pushwoosh saves the segment and adds it to the **External Segments** list right away. ## How the segment stays up to date Pushwoosh doesn’t run your query on a fixed schedule. Instead, it refreshes the segment based on how recently it was last used: * If the segment’s data is less than an hour old, Pushwoosh uses the current data as-is. * If it’s older than an hour, Pushwoosh keeps using the current data while it refreshes in the background. * If it’s older than three hours, Pushwoosh waits for a fresh refresh before it uses the segment. A **Google Analytics 4 audience** segment follows the same rules, with 24 hours and 72 hours instead of one hour and three hours. Google Analytics updates audience membership about once a day. In practice, this means a segment you use regularly (for example, in a recurring journey) stays current, while a segment you rarely touch may take a moment to refresh the first time you use it again. If a refresh fails (for example, the database becomes unreachable or the query starts erroring), Pushwoosh records the error, but the segment’s status on the **External Segments** list doesn’t change on its own. After you’ve fixed the issue on your end, open the segment and click **Check connection** to confirm the fix. The error clears automatically the next time the segment’s data refreshes successfully. ## Use an external segment Once you’ve created it, the external segment automatically appears on **Audience** → **Segments** under the same name, ready to use like any other segment, for example as a campaign or journey audience. To combine it with other conditions, add it as a filter: 1. Go to **Audience** → **Segments** → **Create Segment**. 2. Click **Add filter by** → **Segment**. 3. Select your external segment by name. The same **Segment** filter is available in [compound filters](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters) and when you choose the audience in [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). For more on combining segments, see [Build segments by existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/). ## Check how many people are in the segment The **External Segments** list doesn’t show a member count. Open the segment’s wrapper on **Audience** → **Segments** (see [Use an external segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/#use-an-external-segment)) and [calculate its size](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/calculating-segment-size/) there. ## Manage external segments On the **External Segments** list, each row shows: * The segment’s name. * **Code:** the identifier used to reference the segment in API calls. * The database type. * The host, or the property for a Google Analytics 4 audience. * The status, **valid** or **invalid**. ![External Segments list with a PostgreSQL segment showing name, code, database type, host, and valid status](https://docs.pushwoosh.com/segmentation-external-segments-5.webp) * Click **Edit** to change the connection or query. The database type can’t be changed after creation. Create a new segment instead. * Click **Delete** to remove a segment. Pushwoosh blocks the deletion while the segment is used by a campaign, journey, filter, or popup form. Remove those references first, then delete the segment. This cannot be undone. --- # RFM segmentation **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/ Segment your audience by recency and frequency with RFM analysis, then target each segment with tailored communications. --- With RFM (*Recency, Frequency, Monetary*) Segmentation, you can segment your users based on their engagement level and target these segments with the most tailored communications. RFM segmentation ranks your users from the most loyal ones to those who are churning. It builds a segment for each category of users in your Pushwoosh account. The segmentation is based on two metrics: * **Recency:** how recently users performed a specific [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). * **Frequency:** how frequently they performed that event. In addition, average monetary values (AMV) are calculated for each segment to consider when tailoring your communication strategies. The AMV is the average spend for each user in the segment, whether that’s money, time, or any other countable metric. ## Calculate RFM segments 1. Go to **Audience** → **RFM Segmentation**. 2. Select the **Purchase Event**. Tip If you’ve set up [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/), you can select `PW_Conversion` as the Purchase Event and its `value` attribute as the Monetary Attribute. You don’t need a separate purchase event for RFM. 3. Optionally select the **Monetary Attribute**. Caution For RFM with spend, choose a Monetary Attribute of **Integer** or **Price** type. Other attribute types aren’t supported. For RF only (Recency and Frequency), leave the **Monetary Attribute** field empty. You can use any event, with or without a countable attribute. 4. Click **Calculate**. ![RFM Segmentation interface showing event selection and monetary attribute configuration](https://docs.pushwoosh.com/segmentation-rfm-segmentation-1.webp) Once calculated, RFM segments stay current on their own. They recalculate every 24 hours and also refresh right before a message send or a Customer Journey check that uses an RFM segment. If that refresh isn’t ready in time, or the last calculation failed, the send or journey check uses the last successful calculation. Click **Calculate** anytime you want an immediate update. A recurring journey that uses an RFM segment (for example, every Monday for Champions) does not need a manual recalculation before each launch. ## What the 10 segments mean Pushwoosh scores each user from 1 to 5 for how recently and how frequently they triggered the selected event. 5 is the most recent and frequent. Only events from the last 90 days count toward the score. Older activity is excluded. Users are divided into these 10 segments: * **Champions:** the most active users with the highest recency and frequency scores. * **Loyal customers:** users with the highest frequency of event performance with strong recency scores. * **Potential loyalists:** users who have performed the selected event very recently and have the potential to become loyalists or champions. * **New customers:** most recent users with low frequency scores. Strong candidates to encourage repeat use. * **Promising:** users with high recency scores and the potential to become high frequency users. * **Needs attention:** users with above average frequency and recency scores. * **About to sleep:** users with below average recency and frequency scores. May slip away if not engaged with. * **At risk:** users who have above average frequency but low recency scores. Strong candidates to re-engage. * **Can’t lose them:** these users were active at one point in your app but haven’t performed the event recently. Strong candidates to re-engage. * **Hibernating:** users with the lowest recency and frequency scores. May be lost. For example, set up a Customer Journey for the About to sleep and At risk segments with a re-engaging communication, then recalculate after a while to check whether those users have moved toward the loyal segments. ## Check the RFM grid Check the grid for the number of users in each segment, each segment’s share of your total userbase, and its AMV if you set a Monetary Attribute. From the second calculation on, also check the rate of growth next to the percentage. Check the status above the grid for when segments were last calculated. If you see a yellow banner, refresh them. If you see a red banner, the last calculation failed. ![RFM segments grid displaying 10 user categories with subscriber counts and average monetary values](https://docs.pushwoosh.com/segmentation-rfm-segmentation-2.webp) Click a segment tile in the grid to open the push composer with that segment pre-selected as the audience. ![Segmentation tab of the push composer with the RFM Champions segment pre-selected after clicking the segment in the RFM grid](https://docs.pushwoosh.com/segmentation-rfm-segmentation-3.webp) You do not need to add RFM segments to the Segments list by hand. After calculation, they appear there automatically. Use them wherever you pick a segment, including [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) in Customer Journey. ![RFM segments automatically available in Segments list for targeting](https://docs.pushwoosh.com/segmentation-rfm-segmentation-4.webp) ## Use RFM tags After you calculate, use the **RFM Recency**, **RFM Frequency**, and **RFM Monetary** tags to build your own segments in any combination. ![RFM tags (RFM Recency, RFM Frequency, RFM Monetary) created for custom segment building](https://docs.pushwoosh.com/segmentation-rfm-segmentation-5.webp) Caution Each time you click **Calculate**, these tags update from the latest data on users’ behavior. --- # Understanding subscription forms **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/ How embedded subscription forms add subscribers and tags in Pushwoosh, with guides for setup, styling, double opt-in, and export. --- ## Overview Subscription forms let you collect email subscribers on your website and pass their data directly to Pushwoosh. You can show forms as modal popups or embed them into any page and optionally require email confirmation (double opt-in) so each address is verified before it is added, which helps keep lists accurate. ### When to use subscription forms Use subscription forms to: * Grow an owned email audience without relying on third-party tools * Capture structured profile data (name, phone, custom fields) at the moment of signup * Automatically tag subscribers by form source and build segments for targeted campaigns * Enable double opt-in so subscribers confirm by email before they are added, which helps keep your list clean and engaged * Export subscribers collected via forms so you can target them in campaigns and analytics ### Understand how subscription forms work When you create a subscription form, Pushwoosh automatically creates a tag named **`PW_SubscriptionForm`**, whose value is set to the form code. After you place the embed code on your website and a visitor submits the form: * The form passes the subscriber’s email and other field values to Pushwoosh. Note If this is the first contact for this person, Pushwoosh creates an Email device where **User ID**, **HWID**, and **token** are all set to the email address. If the user already has a **User ID**, the new Email device is linked to that existing User ID and becomes one more device in the profile alongside devices for push, SMS, and other channels. * Pushwoosh assigns the subscription form tag to the subscriber based on the double opt-in setting: * **Double opt-in disabled:** the tag is assigned immediately on submission. * **Double opt-in enabled:** the tag is assigned only after the subscriber confirms their email. Subscribers who do not confirm are not registered in the app and do not receive the tag. * The tag’s value is set to the **form code** used for the subscription. Use this tag to build segments and target subscribers of a specific form. Tip To find the form code, click **Edit** for the form in the forms list and check the URL when the form opens for editing. You can also find the form code in the filename of the CSV from [**Export subscribers**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/#export-subscribers) for that form. ## Set up and manage subscription forms To start working with subscription forms, follow these steps: 1. **Create and embed the form** Set up the form fields, choose whether to display the form as a modal or inline widget, then copy the embed code and add it to your website. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/create-subscription-forms/) 2. **Customize the widget appearance (optional)** Use CSS custom properties on `pushwoosh-subscribe-widget` to adjust colors, typography, spacing, and modal styling. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/customize-widget/) 3. **Enable double opt-in (optional)** Turn on double opt-in for the project, edit the confirmation email, and review how confirmed subscriptions and consent are recorded in **User Explorer**. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/) 4. **Review performance and export subscribers** Use **Form performance** to track views and submissions, manage form status, copy the embed code, and export subscribers as a CSV file. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/) ## What happens when a visitor submits a subscription form The behavior after submission depends on whether [double opt-in](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/#enable-double-opt-in) is enabled and whether the email is already in your project. ### Double opt-in disabled | Scenario | What the visitor sees | What happens in Pushwoosh | | ----------------------------------------- | ------------------------------------------------------ | --------------------------------------------------------------- | | New email | ”Thanks! You’re now subscribed to our emails.” | Subscriber created with status **Confirmed** | | Email already submitted through this form | ”You are already successfully subscribed to the list.” | No changes. The form cannot be resubmitted with the same email. | ### Double opt-in enabled | Scenario | What the visitor sees | What happens in Pushwoosh | | -------------------------------- | --------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | New email | ”Thanks! Please check your email to confirm your subscription.” | Confirmation email sent. The visitor is **not** registered in the app until they confirm. | | Link clicked within 24 hours | Confirmation page | The visitor is registered in the app as a subscriber with status **Confirmed**. | | Link not clicked within 24 hours | ”Link expired. Please return to the form and resubscribe.” | The visitor is **not** registered in the app. The signup attempt has expired. They must submit the form again if they still want to subscribe. | | Email already confirmed | ”You are already successfully subscribed to the list.” | No changes. The form cannot be resubmitted with the same email. | --- # Create and embed a subscription form **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/create-subscription-forms/ Learn how to create a subscription form, choose inline or modal display, embed it on your site, and start collecting subscribers. --- Pushwoosh gives you the flexibility to create subscription forms for different signup scenarios, customize the fields you collect, and display the form as a modal or inline widget on your website. ## How to build a form To create a new subscription form: 1. Go to **Audience → Subscription forms**. 2. Click **Create form**. ![Subscription forms page with Double Opt-In and Subscription forms cards, How to use link, and Create form button](https://docs.pushwoosh.com/subscription-forms-1.webp) 3. Configure the form. A live preview appears on the right and updates as you change settings. ### Configure general settings Configure the following fields. > **All fields in this section are required.** * **Internal form name:** used only inside Pushwoosh, not shown to subscribers. Example: “Newsletter signup”. * **Display type:** choose how the form appears on your site: * **Modal popup:** a popup overlay that appears on top of the page when the **Trigger** you set fires, for example **Immediately** or after a few seconds (see [Set modal popup timing](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/create-subscription-forms/#set-modal-popup-timing)). Good for newsletter signups or promotional offers that you want visitors to notice. * **Embedded inline:** the form sits inside your page content (e.g. in a sidebar or at the bottom of an article). Visitors see it as part of the page layout, not as a separate popup. * **Privacy policy URL:** the form shows a consent checkbox (“I agree to the Privacy Policy”) that links to this URL. Example: `https://example.com/policy`. ![Create Subscription form interface with Internal form name, Display type, Privacy policy URL, and form fields configuration](https://docs.pushwoosh.com/subscription-forms-2.webp) #### Set modal popup timing For **Modal popup** only: 1. Define when the popup appears on the page. In the **Trigger** dropdown select: * **Immediately** (default) * After 3 seconds * After 5 seconds * After 10 seconds 2. In the **Frequency** dropdown select how often each visitor sees the popup: * **Once per visitor** (default): once per device * **Once per session**: once per browser session * **Always**: every time they visit the page ![Create Subscription form with Modal popup display type, Trigger and Frequency dropdowns, and Add field buttons](https://docs.pushwoosh.com/subscription-forms-3.webp) ### Configure form fields Each form includes an **Email** field by default (it cannot be removed). You can add up to four more fields, for a total of five. Click **+ First name**, **+ Last name**, **+ Phone number**, or **+ Custom field** to add them. Note Values from these built-in fields are saved to standard tags: `first_name`, `last_name`, and `phone`. Custom fields save their values to the tags you select in the **Linked tag** setting. For each field, check **Required** if the visitor must fill it in (required fields show an asterisk `*`). Use the trash icon to remove a field. ![Form fields list with Email, First name, Phone number, Last name, City and field configuration for selected field](https://docs.pushwoosh.com/subscription-forms-4.webp) #### Create a custom field 1. Click **+ Custom field** in the field list. 2. Under **Linked tag**, choose the Pushwoosh tag that will store the value submitted from this field (for example `birthday`). You need to create the tag in advance. 3. Set **Field label**: the text visitors see next to the input (for example `Date of birth`). 4. Optionally set **Placeholder (optional)**: hint text inside the empty field (for example `MM/DD/YYYY`). 5. Turn **Required** on or off. ![Custom field configuration with Linked tag, Field label, Placeholder, and Required checkbox](https://docs.pushwoosh.com/subscription-forms-7.webp) ### Customize form labels By default, all form text is in English. To translate the form into another language or change any wording, click **Customize labels** below the live preview. ![Customize form labels dialog with Heading, Button, Privacy Policy Text, Privacy Policy Link Text, Success Message, Double Opt In Message, Email Label, and Email Placeholder fields](https://docs.pushwoosh.com/subscription-forms-16.webp) In the **Customize form labels** dialog, you can edit: * **Heading** * **Button** * **Privacy Policy Text** * **Privacy Policy Link Text** * **Success Message** * **Double Opt In Message** (applies only if double opt-in is enabled) * **Label** and optional **Placeholder** for each form field Click **Save** to apply your changes. They appear immediately in the live preview. The published form updates after you save or publish the subscription form. ### Preview and save the form A live preview appears on the right side of the Create Subscription form interface and updates as you change settings. Use **Reset preview** below the preview to restore the default preview state. Note Test submissions from **Preview** do not create subscribers in your project. ![Create Subscription form with live preview on the right, Reset preview, and Create button in the top right](https://docs.pushwoosh.com/subscription-forms-5.webp) When you finish configuring the form, click **Create** in the top right corner. ### Copy the embed code and add it to your site Developer assistance needed You will need assistance from your development team to add the embed code to your website. A dialog opens with the embed code for your form. Click **Copy code** to copy the snippet to the clipboard. ![Modal form code dialog with script snippet, instruction to paste before closing body tag, and Copy code button](https://docs.pushwoosh.com/subscription-forms-11.webp) #### Where to paste the embed code on your website Add the script to the page HTML where it should run. * For **Modal popup**, placing the script before the closing `` tag is the usual approach. * For **Embedded inline**, put the snippet in the exact place in your markup where you want the form block to appear. Customize the widget with CSS You can override default styling (colors, typography, spacing, and the modal) on your site by setting CSS custom properties on the `pushwoosh-subscribe-widget` element. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/customize-widget/). ### How different display types look on your website These examples show how each form type can appear on a live page. Field labels and required markers match what you configure in the form builder. * Modal popup ![Modal newsletter signup overlay on a webpage with email, name, phone fields, privacy policy checkbox, and Subscribe](https://docs.pushwoosh.com/subscription-forms-14.webp) * Embedded inline ![Inline newsletter signup form in page body with email, name, phone fields, privacy checkbox, and Subscribe button](https://docs.pushwoosh.com/subscription-forms-15.webp) ## Next steps * [Enable double opt-in](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/) to require email confirmation before subscribers are added * [Customize the subscription form widget](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/customize-widget/) to match the form to your website * [Review form performance and export subscribers](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/) to track results and download subscriber data --- # Customize the subscription form widget **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/customize-widget/ Learn how to customize the subscription form widget with CSS variables to match your site's colors, fonts, spacing, and style. --- By default, the subscription form widget uses a neutral style that works on most pages. After you add the [embed code](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/create-subscription-forms/#copy-the-embed-code-and-add-it-to-your-site), you can match it to your site’s visual identity by setting CSS custom properties on the pushwoosh-subscribe-widget element. The values in the tables below replace the defaults. Developer assistance needed You might need assistance from your development team to apply these widget style changes on your website. ## Customize colors | Variable | Description | Default | | -------------------------- | --------------------------------------------- | --------- | | `--esw-primary-color` | Primary color for buttons, links, and accents | `#0E72E5` | | `--esw-primary-hover` | Primary elements hover color | `#1A85FF` | | `--esw-text-color` | Main text color | `#4B5057` | | `--esw-text-secondary` | Secondary text (placeholders, captions) | `#969CA3` | | `--esw-background` | Widget background | `#FFFFFF` | | `--esw-surface-color` | Surface color (hovers, secondary background) | `#F7F9FA` | | `--esw-border-color` | Border color | `#DCE1E5` | | `--esw-border-color-focus` | Border color in focus state | `#0E72E5` | | `--esw-error-color` | Error color | `#E52E59` | | `--esw-success-color` | Success color | `#479900` | | `--esw-success-bg` | Success message background | `#F7F9FA` | ## Typography | Variable | Description | Default | | --------------------------- | -------------------------- | ------------------------------------------------------------------- | | `--esw-font-family` | Font family | `Roboto, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif` | | `--esw-font-size-base` | Base font size | `14px` | | `--esw-font-size-title` | Title font size | `18px` | | `--esw-font-size-label` | Field label font size | `12px` | | `--esw-font-size-small` | Small text (errors, notes) | `12px` | | `--esw-font-weight-regular` | Regular weight | `400` | | `--esw-font-weight-medium` | Medium weight | `500` | | `--esw-font-weight-bold` | Bold weight | `700` | | `--esw-line-height-base` | Base line height | `1.4` | | `--esw-line-height-title` | Title line height | `1.2` | ## Sizes and spacing | Variable | Description | Default | | ----------------------- | ------------------- | ------- | | `--esw-input-height` | Input field height | `36px` | | `--esw-button-height` | Button height | `36px` | | `--esw-spacing-xs` | Extra small spacing | `4px` | | `--esw-spacing-sm` | Small spacing | `8px` | | `--esw-spacing-md` | Medium spacing | `16px` | | `--esw-spacing-lg` | Large spacing | `24px` | | `--esw-popup-width` | Modal width | `380px` | | `--esw-popup-max-width` | Modal max width | `90vw` | ## Appearance | Variable | Description | Default | | --------------------------- | -------------------------- | ------------------------------------------------------------- | | `--esw-border-radius` | Corner radius for elements | `4px` | | `--esw-border-radius-popup` | Modal corner radius | `8px` | | `--esw-shadow-popup` | Modal box shadow | `0 0 6px rgba(0, 0, 0, 0.06), 0 10px 16px rgba(0, 0, 0, 0.1)` | ## Examples * CSS ```css pushwoosh-subscribe-widget { --esw-primary-color: #ff6b6b; --esw-border-radius: 12px; --esw-font-family: "Inter", sans-serif; } ``` * Style attribute ```html ``` --- # Confirm subscribers with double opt-in **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/ Learn how double opt-in works for subscription forms, how to set up the confirmation email, and where consent is logged. --- ## Understand double opt-in Double opt-in is a subscription confirmation method that requires people to verify their email address after submitting a form. Use double opt-in to reduce invalid or mistyped signups, keep your contact list cleaner, and maintain a clearer record of subscriber consent. Note Double opt-in is optional in Pushwoosh and applies to all subscription forms in the project and to the [web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/#confirm-emails-with-double-opt-in) in its web campaigns. Once double opt-in is on, subscribers receive a confirmation email and must click a link to finish signing up. The link expires after 24 hours. If the link expires, the subscriber sees a notification and must return to the form and resubscribe. Note Pushwoosh sends the confirmation message automatically for each subscriber who must confirm. You do not send it manually per subscriber. ### What happens when you turn double opt-in on or off ##### Turning double opt-in on * Subscribers who signed up **before** you enabled double opt-in keep their existing status (for example **Confirmed**) and do not receive new confirmation emails. * All new signups after you enable double opt-in receive a confirmation email and stay in **Pending** until they confirm. ##### Turning double opt-in off * Subscribers who signed up while double opt-in was enabled and are still in **Pending** are **not** automatically promoted to **Confirmed**. They must either confirm via the email they received (if the link is still valid) or submit the form again. * All **new** signups after you disable double opt-in are recorded as **Confirmed** immediately and do not receive confirmation emails. ## How to configure double opt-in ### Before you start: verify a sending domain Double opt-in requires at least one verified domain in your Pushwoosh account. Confirmation emails are sent from your own domain, not from a Pushwoosh address. To get one, [add a domain](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#add-a-domain), add its DNS records, and wait until the domain status is **Verified**. While double opt-in is on and the account has no verified domain: * You cannot create a subscription form or save changes to a form’s configuration. * Visitors who submit a form get an error, and Pushwoosh does not add them as subscribers. The error text is `double opt-in requires at least one verified email sending domain`. To fix it, verify a domain. You do not need to change the form. ### Enable double opt-in The **Double Opt-In** card is on two pages, and both control the same setting: * **Audience** → **Subscription forms**. * The email platform settings. Go to **Settings** → **Configure Platforms** and click **Connect** or **Platform settings** in the **Emails** row. The card appears there once a sending domain is added to your account. Until then, this page shows the email setup steps instead of the card. While double opt-in is on and the account has no verified domain, the card shows a warning. To turn on double opt-in, click **Enable** on the card. ![Subscription forms page showing Double Opt-In card with Enable control and subscription forms area](https://docs.pushwoosh.com/subscription-forms-12.webp) When you click **Enable**, a confirmation dialog opens with information you should review before turning on double opt-in. Click **Enable** in the dialog to continue. ![Enable Double Opt-In modal with description, consent-related information, and Enable button](https://docs.pushwoosh.com/subscription-forms-6.webp) ### Customize the confirmation email content When you enable double opt-in on the **Audience** → **Subscription forms** page, the confirmation email editor opens so you can customize the message for your brand and campaign. On the email platform settings page, the editor doesn’t open by itself. Click **Edit email content** on the **Double Opt-In** card instead. The template already includes the confirmation button and the unsubscribe notice. Complete this template once before your forms start sending confirmation emails. Add company name, physical address, and links to your Privacy Policy and Contact Us pages. Note The confirmation email template is shared across all subscription forms and web forms in the project. You cannot use different templates for different forms. ![Double opt-in confirmation email in Unlayer editor with subject, body, Confirm your email button, and button settings panel](https://docs.pushwoosh.com/subscription-forms-13.webp) In the editor: * Set **Subject** for the confirmation message. * Add your company name and address. * Include links to your Privacy Policy, Contact Us, and Unsubscribe pages. Pushwoosh does not use the **From name** and **From email** set in this editor. The confirmation email is sent with the project’s [default sender details](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#add-default-sender-details). If the project has none, the sender is a no-reply address on the earliest verified domain in your account. Important The confirmation button is ready to use. It already includes the merge field for the confirmation URL. Pushwoosh replaces it with the real link when the email is sent. Tip For multiple languages, use the same editor steps as for other email content. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#define-the-languages-you-want-to-use-in-your-email). ## Consent logging in User Explorer for double opt-in When double opt-in is enabled for subscription forms, Pushwoosh logs consent-related details in the subscriber profile in [**User Explorer**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). You can use this information for internal reviews, to answer consent questions in support, or when your own legal and privacy teams need a record of what the product captured. Pushwoosh records the following details in the subscriber profile: * Form submission timestamp * Confirmation timestamp (when the subscriber clicks the confirmation link) * IP address * Method of consent (subscription form with double opt-in) For export and form metrics, see [View form performance and export subscribers](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/). --- # View form performance and export subscribers **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/ Learn how to review subscription form performance, manage form status, and export subscriber data as a CSV for analysis. --- After your subscription forms are live on your site, compare their performance, identify which ones bring more subscribers, and export subscriber data for analysis or follow-up. You can also pause active forms, reactivate paused ones, and copy the embed code when you need to update your website. ## Manage forms The **Form performance** table shows all forms and their metrics. Use the three-dot menu on each row to manage a form. Here you can: * **Edit:** update form settings and fields * **Copy code:** copy the embed code snippet to add the form to your website * [**Export subscribers**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/form-analytics-and-export/#export-subscribers): open the export dialog to download subscriber data as CSV * **Pause:** temporarily stop showing the form (for active forms). For paused forms, the menu shows **Activate** to resume. ![Form performance table with form metrics and row menu open showing Edit, Copy code, Export subscribers, and Pause](https://docs.pushwoosh.com/subscription-forms-10.webp) ## Review form metrics The table helps you compare how each subscription form performs and understand how visitors interact with it. Use these metrics to see how often a form is shown, how many visitors submit it, and how effectively it turns views into subscriptions. The table has the following columns: | | | | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Form name** | The name you gave the form, with **Modal** or **Inline** shown on the second line under the name. | | **Status** | **ACTIVE** or **PAUSED**. | | **Viewed** | How many times visitors were shown the form. Counts **impressions**, not necessarily unique visitors. For **modal popup**, a view is counted each time the modal appears on screen, even if the visitor closes it without entering data or submitting. For **embedded inline**, a view is counted when the form becomes **visible on the visitor’s screen**, for example after they scroll to it or if it is already visible when the page loads. | | **Submissions** | How many visitors submitted the form. | | **Conversion rate** | Percentage of form views that resulted in a submission: (Submissions / Viewed) × 100. Counts events, not unique visitors. | | **Confirmation rate** | Percentage of subscribers who confirmed their email address. Only shown when double opt-in is enabled. | | **Created** | When the form was created. | ![Form performance table with one form in PAUSED status and Activate in the row menu](https://docs.pushwoosh.com/subscription-forms-9.webp) ## Export subscribers Exporting subscribers requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. 1. In the Form performance table, open the three-dot menu for the form. 2. Click **Export subscribers**. The **Export subscribers** dialog opens. 3. Configure export options: * **Filter:** **All subscribers**, **Pending only**, **Confirmed only**, or **Expired only** * **Date range to export:** select the date range for the data you want to export 4. Click **Export**. ![Export subscribers dialog with Filter options, date range selector, and Export button](https://docs.pushwoosh.com/subscription-forms-8.webp) When the export is ready, a notification appears at the top of the page. Click **Download CSV** to download the file. CSV export columns The header row of the export uses these columns: * **email** * **first_name** * **last_name** * **phone** * **custom_fields:** values from custom fields on the form * **ip** * **status:** for example **Confirmed**, **Pending**, or **Expired** when double opt-in is enabled. A visitor is **not** registered in the app until **Confirmed**. **Pending** and **Expired** never apply when double opt-in is disabled. When double opt-in is disabled, a submission is recorded as **Confirmed** immediately. * **submitted:** submission timestamp * **confirmed:** confirmation timestamp after double opt-in, empty when not applicable --- # Telecom starter for subscriber data and segments **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/telecom-starter/ Set up a telecom subscriber profile with the right tag types, update it server-to-server, and build bundle and balance segments. --- This guide sets up a telecom subscriber profile in Pushwoosh and turns it into working segments: bundle expiry reminders, low balance alerts, top-up confirmations, and roaming welcome messages. Complete every section and the first segment you build returns a non-zero audience, so you do not have to contact support. The single most common mistake is the tag type. A date stored in an Integer tag looks fine in the tag list and silently makes every date segment return zero users. Choose the types first, then load the data. ## Prerequisites * An application in your Pushwoosh account, with the SDK integrated or devices registered through the API. * An [API access token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) with permission to set tags. * Developer assistance for the server-to-server update job. * A subscriber identifier you can map to Pushwoosh: either a User ID (usually the MSISDN, the subscriber’s phone number in international format, or an internal subscriber id) or the device HWID. ## What a telecom subscriber profile looks like The table below lists the tags that cover the standard telecom scenarios. Create them before the first data load, with exactly these types. | Tag | Type | Example value | What it drives | | -------------------- | ------- | --------------------- | ----------------------------------------------------------- | | `msisdn` | String | `923001234567` | Identity and SMS targeting | | `tariff_plan` | String | `Gold Postpaid` | Plan-specific offers | | `prepaid_postpaid` | String | `prepaid` | Splitting the base by billing model | | `balance` | Integer | `50` | Low balance alerts | | `bundle_id` | String | `DATA_5GB_30D` | Which bundle the reminder is about | | `bundle_expiry_date` | Date | `2026-09-20 21:00:00` | Bundle expiry reminders | | `roaming_status` | Boolean | `true` | Roaming welcome messages and warnings about roaming charges | Two rows in that table decide whether the scenarios work at all. * `bundle_expiry_date` has to be a Date tag. Only Date tags get the relative operators, such as `between N and M days ahead`, that express “the bundle expires in three days” without recalculating the segment every night. * `bundle_id` stays separate from the expiry date. One tag holds the date, another holds which bundle it belongs to. Storing both in one tag would require parsing a string inside the segment, which the segment builder cannot do. ## Why the tag type is decided before the first upload Tags are created automatically the first time a value arrives, and the type is inferred from that first value. A whole number becomes Integer, a number with a decimal point becomes Price, a string becomes String (or Date, if it matches a recognized date-time format such as `2024-10-02 22:11`), an array becomes List, and `true`/`false` becomes Boolean. The inference fails on telecom data, because expiry dates are usually sent as Unix timestamps: * You send `bundle_expiry_date` as the number `1758393600`. It is a whole number, so the tag is created as an Integer tag. Values load correctly, the tag looks healthy, and no date operator is ever offered for it. * The tag already exists as Integer and you later switch to sending `"2026-09-20"`. The value no longer parses as a number, so it is dropped without any error. The API still answers with success, and the device keeps its old value or none at all. Both cases end with a segment that returns zero users and no error anywhere to explain it. A tag type cannot be changed after creation. Fixing a wrong type means creating a new tag with the correct type and reloading the values into it. The old tag stays in the list until you delete it. To prevent both cases, set the types yourself: 1. Open the **Tags** page of your Control Panel. 2. Click **Create tag**. 3. Enter the tag name and choose its type from the list. Repeat for every tag in the table above, before the first upload. 4. In `bulkSetTags`, send `create_missing_tags: false`. A missing tag then returns an error instead of being created with a guessed type. ## How to update the profile server-to-server Telecom profile data changes daily, so it is loaded as a batch job rather than from the mobile SDK. 1. Build the daily delta on your side: subscribers whose balance, bundle, or roaming status changed since the last run. A full base reload every night is rarely needed and costs you request volume. 2. Send the batch to [`bulkSetTags`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags), addressing devices by `user_id` when the MSISDN is your User ID, or by `hwid` otherwise. One request carries many devices, and the method expects at least 50 of them. For a single subscriber, use [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) instead. 3. Poll the returned `request_id` with [`bulkSetTags` status](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulksettags-status) until the job finishes. Request it with `?detailed=true` and log the result, because a finished job is not the same as every value being accepted. 4. Retry failed batches with the same payload. Setting a tag is idempotent: sending the same value twice leaves the same profile. Daily bundle update ```json { "application": "XXXXX-XXXXX", "auth": "your API access token", "create_missing_tags": false, "devices": [{ "user_id": "923001234567", "tags": { "bundle_id": "DATA_5GB_30D", "bundle_expiry_date": "2026-09-20 21:00:00", "balance": 50, "roaming_status": false } }] } ``` ### Which date formats a Date tag accepts A Date tag stores a Unix epoch timestamp in seconds. Send one of these: * An epoch value in seconds, as a number: `1758393600`. * A date-time string with separators: `2026-09-20 21:00:00`, `2026-09-20 21:00`, or `2026-09-20`. A date without a time means midnight. * An ISO 8601 string with an offset: `2026-09-20T21:00:00+05:00`. Two formats behave in a way that surprises most integrations: * **A string without a time zone is read as UTC.** It is not read in your local time. A bundle that expires at 21:00 in Karachi is `2026-09-20T21:00:00+05:00`, or the matching epoch value. `2026-09-20 21:00:00` is three hours earlier in real time, which moves subscribers between daily reminder waves. * **A string of digits is an epoch value, not a date.** `"20260920"` is not September 20, 2026, it is an epoch timestamp pointing at 1970. Send either a real epoch value or a string with separators. A value that matches none of the accepted formats is discarded without failing the request. That is why step 3 above checks the job result rather than the HTTP status alone. ## Segment recipes Each recipe below is one segment. Open the **Segments** section, click **Create Segment** to open the builder, then add the filters listed. For the full builder walkthrough, see [Create segments by tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/). ### Bundle expires in three days Targets subscribers whose current bundle runs out in three days, so the reminder arrives while a renewal still makes sense. * **Tag:** `bundle_expiry_date` * **Operator:** open the operator list, go to the **RELATIVE DATES** section, and choose `between N and M days ahead` * **Values:** `3` and `3` Change both values to `1` and `1` for the last-day reminder. Add a second filter on `bundle_id` when the message names the specific bundle. Do not use the ANNIVERSARY operators here The **ANNIVERSARY** section offers `is in N days`. That operator compares the day and the month only, so it matches every subscriber whose bundle expired on that date in any year. Use it for birthdays, not for bundle validity. ### Low balance Targets prepaid subscribers who can no longer pay for the next renewal. * **Tag:** `balance`, operator `less or equals`, value `50` * **Tag:** `prepaid_postpaid`, operator `is`, value `prepaid` Both conditions go in the same group, combined with **AND**. ### Roaming entry Targets subscribers who are currently abroad, for a welcome message with local rates. * **Tag:** `roaming_status`, operator `is`, value `true` A tag-based segment reflects the state at compile time. When you need the message to go out the moment roaming starts, trigger a customer journey from a roaming event instead of sending to this segment. ### Top-up confirmation and other reactions Confirming a top-up is a reaction to a single subscriber’s action, not an audience to compile. Send a custom event from your billing system with [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) and start a [customer journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) from it. The same applies to bundle purchase and plan change. ## The segment returns zero users Check these in order. The first three cover most of the cases reported to support. 1. **Check the tag type on the Tags page.** If `bundle_expiry_date` is Integer, no date operator was ever applied and the segment compared numbers. Create a Date tag and reload the values. 2. **Check that values actually arrived.** Open [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/), find a subscriber you know was in the batch, and look at their tags. An empty tag after a successful job means the values were rejected by format, most often digits-only strings or a date that no layout matched. 3. **Check the operator section.** `is in N days` under **ANNIVERSARY** ignores the year. `between N and M days ahead` under **RELATIVE DATES** does not. 4. **Check the time zone.** Expiry timestamps sent without an offset are read as UTC, which can shift a subscriber into the previous or next day of your reminder schedule. 5. **Recalculate the segment** before reading the number, so you are not looking at a cached size. See [Calculating segment size](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/calculating-segment-size/). ## Limitations to account for * **A tag type is permanent.** Plan the profile before the first upload, because fixing a type later means a new tag and a full reload. * **Relative date operators are unavailable in high-speed delivery segments.** Applications configured for [high-speed delivery](https://docs.pushwoosh.com/product/resources/high-speed-delivery/) precompile their segments, and the relative date operators are not offered there. Bundle expiry reminders must run as ordinary segments. * **A batch job is not real time.** Segments see the profile as of the last successful load. Scenarios that must fire within seconds of a balance change belong in an event-triggered journey, not in a nightly batch. --- # Understanding user data (Tags) in Pushwoosh **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/ Upload, save, and update user attributes with Pushwoosh Tags. Use them to send targeted messages across channels and improve campaign performance. --- Read the documentation on Tags: * [How to set up Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) * [How to update Tags via CSV](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/) You can also watch a video on working with Tags and Segments: [Youtube Video: How to work with Tags and Segments](https://youtube.com/watch?v=tahgIHOUGc0) --- # Manage user data with tags **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/ Tags are powerful tools for audience segmentation and targeted messaging. Use them to send personalized push notifications to specific user groups. --- [Youtube Video: Tags are one of the most useful tools we offer. A lot of sophisticated Pushwoosh functionality is based on this feature. Using tags, you can segment your audience and send push notifications to some users, but not others.](https://youtube.com/watch?v=tahgIHOUGc0) Tags are one of the most useful tools we offer. A lot of sophisticated Pushwoosh functionality is based on this feature. Using tags, you can segment your audience and send push notifications to some users, but not others. Deciding which tags to use First, think about your business needs and decide how you want to segment your audience. Do you need to target users based on age, location, in-app purchase history, etc.? Once you’ve decided on segmentation logic, your developers can add tags to your app’s code. This will then allow you to send targeted messages to your users. Tags contain any arbitrary data that you associate with a particular user or device. This includes username, ID, city, favorite football team, preferred news categories, or any other piece of information about your users. Let’s take a closer look. ## Tag values Tag values can help you make your push campaigns smarter. Each tag is capable of storing *nearly limitless number of values*. Basically, this means that one tag would be enough to record a specific type of information about each end-user in your database. ## Types of tags * **Integer** — used for the integer data (amount of in-game cash acquired, level achieved, age). * **String** — used for string values (username, e-mail, identifiers). * **List** — the same as String type, but each user may have multiple values set simultaneously (music preferences, news categories, cuisine preferences). * **Boolean** — true / false type of Tag. * **Date** — used for calendar dates. Basically, this is an integer type tag which stores Unix Epoch timestamps (automatically converted from/into Gregorian date). * **Price** — allows to set values according to specified currency in the “*.XX” format (). * **Version** — used for versioning. The example of allowed format is w.x.y.z (Major.Minor.Patch.Build). Max value for each version part is 9999, so the max version number cannot be greater than 9999.9999.9999.9999. ### Tag operators Each Tag type has a specific set of **operators** applicable. Tag operators define the relationship between the Tag and its values for segmentation purposes. * Integer Tag operators: `is`, `is not`, `greater or equals`, `less or equals`, `between`, `in`, `not in`, `not set`, `any` * String Tag operators: `is`, `is not`, `starts with`, `ends with`, `contains`, `in`, `not in`, `not set`, `any` * List Tag operators: `in`, `not in`, `not set`, `any` * Boolean Tag operators: `is` (true/false), `not set`, `any` * Date Tag operators: `during the last N days`, `exactly N days ago`, `N or more days ago`, `between N and M days ago`, `between N and M days ahead`, `is today`, `is in N days`, `is N days past`, `on a specific date`, `on or after a specific date`, `on or before a specific date`, `between dates`, `not set`, `any` * Price Tag operators: `is`, `is not`, `greater or equals`, `less or equals`, `between`, `in`, `not in`, `not set`, `any` * Version Tag operators: `is`, `is not`, `greater or equals`, `less or equals`, `between`, `in`, `not in`, `not set`, `any` Note “Not set” and “any” operators are available for all types of Tags. ## Tag scope: General vs User-specific When creating a tag, you choose how its values are stored: * **General**: the tag value is stored per device (HWID). Each device of the same user can hold a different value independently. * **User-specific** (selected by default when you create a tag in the Control Panel): the tag value is stored per user (UserID). When set via UserID, the value is applied to all of the user’s devices at once. Useful for attributes that belong to the person, not a particular device: subscription tier, loyalty points, preferred language. ## Default tags These tags are available by Pushwoosh out-of-the-box, so you don’t have to (and, in fact, should not) set them manually. Most of them are set from the application and sent over to our server via [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) and other API calls, and some are set by the server itself. **SMS Marketing Consent** and **Viber Marketing Consent** are an exception. Your developers set them with [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) when a person agrees to or declines marketing messages. | Name | Type | Where it’s set | Description | | ----------------------------- | ------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Application Version | Version | SDK | Current version of the app installed on a device | | Browser Type | String | SDK | When a device is registered for your web project, its type – mobile or desktop – is tracked automatically | | City | String | Server | Latest registered geographical location of a device | | Country | String | Server | Latest registered geographical location of a device | | Device Model | String | SDK | Indicates the device model whereon the app is installed | | First Install | Date | Server | Indicates the time when a device was registered for notifications for the first time | | In-app Product | List | SDK | The in-app products purchased by a user of the app | | Last In-app Purchase date | Date | SDK | The date of the latest in-app purchase made on a device | | Language | String | SDK | Two-letter lowercase abbreviation of a device’s locale according to ISO-639-1; taken from device settings | | Last Application Open | Date | Server | The time of the most recent app launch on a device | | Last Email Open | Date | Server | The date when the device’s email address most recently registered an email open event. Opens by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t update it | | Last Email Open Message Code | String | Server | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers#message-code) of the most recently opened email (format `XXXX-XXXXXXXX-XXXXXXXX`). Updated on each [`PW_EmailOpen`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailopen) event, so opens by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t update it. Use it to segment recipients of a specific email campaign by who opened it | | Last Email Click | Date | Server | The date when the device’s email address most recently registered an email link click. Clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t update it | | Last Email Click Message Code | String | Server | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers#message-code) of the most recent email in which a link was clicked (format `XXXX-XXXXXXXX-XXXXXXXX`). Updated on each [`PW_EmailLinkClicked`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emaillinkclicked) event, so clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t update it. Use it to segment recipients of a specific email campaign by who clicked | | Last Email Confirm | Date | Server | The date of the most recent Double Opt-In subscription confirmation for the device’s email address | | Bounced Email | Date | Server | The date when a hard bounce occurred for this email address. Stored as Date to enable time-based segmentation, for example, to exclude users with recent bounces. | | Unsubscribed Emails | Boolean | SDK | Indicates whether a user has unsubscribed from receiving emails from your app | | SMS Marketing Consent | Boolean | Server, setTags | Whether the SMS subscriber agreed to marketing SMS: `true` agreed, `false` opted out, no value unknown. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/) | | Viber Marketing Consent | Boolean | Server, setTags | Whether the Viber subscriber agreed to marketing Viber messages: `true` agreed, `false` opted out, no value unknown. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/) | | OS Version | Version | SDK | The version of an operating system running on a device | | Platform | String | SDK | The platform on which the user is using your project. | | Push Alerts Enabled | Boolean | SDK | Indicates whether push alerts are allowed in device settings | | Push Permission Status | String | SDK | State of the push permission on a device, such as `not_determined`, `provisional`, `granted_explicit`, or `denied`. Reserved: you can’t create a custom tag with this name. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) | | SDK Version | Version | SDK | The version of the Pushwoosh SDK implemented on a device | | Live Activity ID | List | SDK | Used to manage live activities by associating a Live Activity ID with the device. Automatically cleaned up after 12 hours. Reserved: you can’t create a custom tag with this name. | | WhatsApp Session Expires At | Date | Server | Holds the current 24-hour [customer service window](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/#send-free-form-messages-and-view-your-whatsapp-conversations) opened by the customer’s last inbound WhatsApp message. Set automatically on each inbound message. Reserved: you can’t create a custom tag with this name. Stores only the current window, no history. | | WhatsApp Ad Click ID | String | Server | The Click-to-WhatsApp ad click ID (`ctwa_clid`) from the [referral](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_whatsappreceived) of an inbound WhatsApp message. Meta sends it once, on the message that opens the conversation. A newer click overwrites it together with `WhatsApp Ad Click Date`. A replayed or out-of-order webhook doesn’t roll it back. | | WhatsApp Ad Click Date | Date | Server | The time of the click recorded in `WhatsApp Ad Click ID`. Updated together with it: a newer click overwrites the previous date. | ## Custom Tags This is where your creativity comes in to achieve your specific business goals. Custom tags can be created based on the segmentation logic or targeting pattern that is appropriate for your unique business needs. ### How to set up a custom tag Developer assistance needed You’ll need help from your development team to set up custom tags. Creating a tag here is optional. Pushwoosh also creates a tag automatically the first time your developers send its name from the app or API, and infers its type (Integer, Price, String, Date, List, or Boolean) from the value. An auto-created tag is always **User-specific**. Create the tag here first if you need a Version tag, which auto-creation can’t infer, or a **General** tag. Tag auto-creation is enabled by default for the vast majority of accounts. If a tag your developers send doesn’t show up automatically, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to check whether auto-creation is turned off for your account. To begin, create and configure tags in the Pushwoosh Control Panel: 1. Go to the **Tags** page of your Control Panel and click **Create tag**. 2. Enter the tag name and pick the value type. 3. Choose the [tag scope](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#tag-scope-general-vs-user-specific). The **User-specific** checkbox is selected by default, which stores the value per user. Clear it to make a **General** tag that stores the value per device. 4. Click **Create**. Developer assistance needed At this point, the custom tag is ready, but it will require your developer to implement it in the app to start collecting information from users. Please share [this](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/tags#custom-tags) guide with your development team. #### Using the default City tag Device’s location is determined based on its IP address at the moment your app was launched on that device for the last time. GeoIP submits the location data to Pushwoosh, and Pushwoosh saves the location received from GeoIP as a City tag value for a particular device. In some cases, the location submitted by the GeoIP differs from the city name — for example, when it refers to an area of a city or other administrative unit. Please be careful when using the default City tag for segmentation purposes: make sure to select the proper values. For example, if you’re going to target users from Munich, you have to cover it with a bunch of City tag values, including “Munich” itself (with all the corresponding values, such as different variants of spelling which could be returned by GeoIP and saved as tag values) and several nearby areas. ## Tags statistics Once your application sets Tag values, you can view and analyze the statistics about these values, presented in the Control Panel using either a pie chart, bar chart, or table view. ![Tags statistics dashboard showing pie chart, bar chart, and table view options for analyzing tag values](https://docs.pushwoosh.com/audience-data-and-segmentation-user-data-tags-tags-1.webp) ## Delete tags To delete several tags at once: 1. In the **Tags** section, click **Bulk actions** → **Delete…**. 2. Select the tags. 3. Click the **Delete** button above the list. The dialog lists the tags that will be deleted and skips the ones still used in segments, journeys, messages, presets, or other content, as well as tags the platform uses. Deleted tags and their values can’t be restored. --- # Update tags via CSV **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/ Assign Tags to your subscribers' devices manually by uploading a CSV file --- Upload a CSV file to set the values of several Tags for many devices at once, for example to prepare data for segmentation. You can only update Tags that already exist in your account. The import doesn’t create new ones. [Youtube Video: Assign Tags to your subscribers' devices manually by uploading a CSV file](https://youtube.com/watch?v=tahgIHOUGc0) ## 1. Prepare CSV Export a segment and edit it, or create the file yourself. ### Export segment Go to [Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation), select the Segment you’d like to set tags for and [export the segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation). The file contains: * HWIDs of devices complying with that Filter segment (in the first column of the .csv) * Push Tokens of user’s devices (in the second column) * types of devices (integer and humanized) * all Tags from your Pushwoosh account (in the remaining columns) Change Tag values or add missing ones in the file, then save it. ### Create CSV To create the file yourself, set up these columns: * **Identifier column:** name it `HWID` and fill it with device hardware IDs, or name it `User ID` and fill it with user IDs. * **Tag columns:** name each one exactly as a Tag in your Pushwoosh account. Each cell holds the value to set for that device or user. | HWID | Name | Age | Push Alerts Enabled | SDK Version | First Install | Preferences | | -------------------------------- | ---- | --- | ------------------- | ----------- | ------------------- | --------------- | | XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX | Paul | 25 | false | 6.4.12.0 | 2023-06-27 12:00:00 | sport, news | | YYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY | Anne | 30 | true | 6.7.17.0 | 2025-11-14 16:00:00 | fashion, travel | Representation of `import.csv` in plain text format: ```txt HWID,Name,Age,Push Alerts Enabled,SDK Version,First Install,Preferences XXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX,Paul,25,false,6.4.12.0,2023-06-27 12:00:00,"sport, news" YYYY-YYYY-YYYY-YYYY-YYYYYYYYYYYY,Anne,30,true,6.7.17.0,2025-11-14 16:00:00,"fashion, travel" ``` #### List Tag values A List Tag cell accepts two formats: * **Comma-separated list:** `sport, news`, as in the example above. This format is easier to produce in spreadsheet apps. Values are always split on the comma. * **JSON array:** `["sport","news"]`. Use it when a single value contains a comma, for example `["Sports, general","news"]`. Leaving a List Tag’s cell empty keeps the subscriber’s existing value for that Tag unchanged. To clear the subscriber’s value for a List Tag, use an empty JSON array (`[]`) instead. For Tags of other types (String, Integer, Date), an empty cell is imported as-is and overwrites the subscriber’s existing value. ## 2. Upload CSV Caution Before you upload, make sure that: 1. The file is saved in **UTF-8** encoding. 2. Values are separated by a **comma**. 3. Every row has the same number of columns as the header, and every quoted value is closed. A single malformed row gets the whole file rejected, not just that row. 1. Select the project. Tag values are updated in this project only. If an identifier isn’t registered in this project, its row gets no Tag values. 2. Go to **Import CSV** → **Update Tag values**. ![Import CSV interface showing Update Tag values option](https://docs.pushwoosh.com/audience-data-and-segmentation-user-data-tags-update-tags-via-csv-1.webp) 3. Click **Upload CSV** and select your file. ![Upload CSV file browser interface](https://docs.pushwoosh.com/audience-data-and-segmentation-user-data-tags-update-tags-via-csv-2.webp) Note Columns that aren’t named exactly as Tags in your account are skipped. Pushwoosh checks the whole file for parse errors when you upload it. A row that can’t be parsed (its column count doesn’t match the header, or a quoted value isn’t closed) gets the file rejected right away. A toast names the row and the reason, for example “Row 42: the number of values does not match the header.” Nothing is imported in this case. Fix that row and upload the file again. ## 3. Choose Tags to update 1. Choose the identifier type your CSV uses, **HWID** or **User ID**, and the column that contains it. The type decides which Tags can be updated: * **HWID:** device-specific Tags, such as SDK Version. * **User ID:** user-specific Tags, whose values belong to the user profile rather than a device. Pushwoosh shows which Tags will be imported and which will be skipped for the type you picked. 2. Select the columns to import. 3. Click **Start import**. ![Tag selection interface showing identifier type, columns to import, and Start import button](https://docs.pushwoosh.com/audience-data-and-segmentation-user-data-tags-update-tags-via-csv-3.webp) ## 4. Check the results The import status updates in real time. Large files can take a while, so you can leave the page and check back later. When the import completes, the report confirms that tag values from the selected columns were updated. Check its counts: * **Matched:** CSV rows, not devices, whose identifier found at least one device. * **Updated:** devices, not rows. In a User ID import, one row can match several of the user’s devices, so this number can be higher than **Matched**. * **Updated users:** in a User ID import only, the number of distinct users with at least one updated device. Two rows for the same user count as one. Note For a file over 100,000 rows, Pushwoosh processes it in batches and can count the same user as updated in more than one batch if that user’s rows land on both sides of a batch boundary. The Updated users count can then be slightly higher than the true number of distinct users. A row is skipped for one of two reasons: * **No device matches the identifier.** Pushwoosh checks it against HWID, User ID, or push token, regardless of which identifier type you chose in step 3. * **The identifier column is empty.** The row never reaches device matching. Matched rows plus skipped rows should add up to the total rows in the file. ![Import results dashboard showing matched rows, updated devices, and rows skipped for each of the two skip reasons](https://docs.pushwoosh.com/audience-data-and-segmentation-user-data-tags-update-tags-via-csv-4.webp) Caution In rare cases, the import fails after it starts, even though Pushwoosh checked the file at [upload](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/#2-upload-csv). This happens if the file changes between upload and **Start import** and a row can’t be parsed (its column count doesn’t match the header, or a quoted value isn’t closed). The status then shows **Failed** instead of **Completed**. The report names the row, for example “Row 42: the number of values does not match the header.” No tag values are applied, including for rows before the broken one. Fix that row and upload the whole file again. --- # User Explorer **URL:** https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/ Explore individual user profiles with device and platform data, message history, segments, and campaign participation for debugging and segmentation. --- The User Explorer section provides a detailed view of your audience, allowing you to explore individual user data, including their devices, received messages, and interactions. It gathers and organizes customer data from multiple touchpoints, structuring real-time insights into centralized user profiles. This enables effective debugging, user segmentation, and targeted marketing strategies. Opening User Explorer requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. ## Browse recent and test devices While the search field is empty, User Explorer shows two tabs under the search bar: **Recent devices** and **Test devices**. Use them to open a user profile without knowing a User ID or HWID. ![User Explorer with an empty search and the Recent devices tab listing User ID, HWID, Platform, and Registered columns](https://docs.pushwoosh.com/userexplorer-16.webp) ### Recent devices tab The **Recent devices** tab opens by default. It lists up to 10 of the most recent devices that registered in this application in the last 7 days, newest first. A registration is the [`PW_DeviceRegistered`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) event, and each device appears once. Each row shows the device’s **User ID**, **HWID**, **Platform**, and **Registered** date and time. Click a User ID to open that user’s profile. For an anonymous user, whose app hasn’t set a [custom User ID](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/), the **User ID** column shows the device’s HWID. A dash means the registration came without a User ID. The list loads when you open User Explorer. To see devices that registered after that, refresh the page. If no device registered in the last 7 days, the tab shows **No devices in the last 7 days**. ### Test devices tab The **Test devices** tab lists the application’s [test devices](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/), 10 per page. Each row shows the **Device name** with its description, **HWID**, **Platform**, and **Date added**. Click a device name to open the profile of the user that device belongs to. A name without a link means Pushwoosh has no subscriber with that HWID in this application. ![Test devices tab in User Explorer with a paginated table of device names, HWIDs, platforms, and dates added](https://docs.pushwoosh.com/userexplorer-17.webp) To add, remove, or search test devices, click **Manage test devices** in the page header. It opens the [**Test Devices**](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/) page in Settings. ## Search for a user 1. Go to **Audience** → **User Explorer**. 2. In the search bar, select a criterion from the dropdown: * **User ID:** search by [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) * **HWID, Email, Phone:** search by [HWID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#hwid), email address, or phone number * **Token:** search by [push token](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#push-token) 3. Enter the value for the selected criterion. 4. Click **Search**. Tip By default, **User ID** is equivalent to a **Hardware ID (HWID)**, but your development team can set a **custom User ID** as described in [Collecting User IDs](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/). Matching users appear in the results list under **USER ID**. Click the name to open the user profile. ![User Explorer search dropdown showing User ID, HWID Email Phone, and Token search criteria options](https://docs.pushwoosh.com/userexplorer.webp) A **Test device** badge next to the User ID means at least one of this user’s devices is a [test device](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/). ![User Explorer search result with a Test device badge next to the User ID and a Clear request link under the results](https://docs.pushwoosh.com/userexplorer-18.webp) To return to the **Recent devices** and **Test devices** tabs, click **Clear request** under the results, or the **×** in the search field. ## View user profile The **View user** page opens when you select a user from the search results. Use the tabs at the top to review profile data: * [**User overview**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#user-overview-tab): view user-level tags and edit tag values * [**Devices and platforms**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#devices-and-platforms-tab): review channels, set test devices, or manage a channel * [**Events history**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab): search and filter events, view event attributes * [**Received messages**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#received-messages-tab): find messages by code and open delivery details * [**Inbox**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#inbox-tab): see what’s currently stored in the user’s [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) * [**Segments**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#segments-tab): see segment membership and open segment settings * [**Campaigns**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#campaigns-tab): see [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) entry, status, and exit dates ### User overview tab The **User overview** tab shows the user’s **User ID** and a table of [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) assigned at the user level. The table includes every tag that has a value for this user. ![User overview tab showing User ID and a tags table with default, custom, and PW system tag values](https://docs.pushwoosh.com/userexplorer-3.webp) #### Edit tag values To edit a tag value in the table: 1. Find the tag in the table. 2. Click the **pencil icon** next to the value. 3. Enter a new value. 4. Click **Submit** to save changes or **Cancel** to discard them. ![User overview tab with pencil edit icons next to tag values in the tags table](https://docs.pushwoosh.com/userexplorer-6.webp) #### Erase personal data To permanently erase a subscriber’s personal data for the current application: 1. Click the **three-dot menu (⋮)** next to the User ID. 2. Select **Erase personal data**. 3. Leave **Count the volume first** checked (the default) to see how many records match before anything is deleted, or clear it to erase directly. Clearing the checkbox changes the primary button from **Count** to **Erase data**. 4. Type the identifier shown in the dialog to confirm. The button stays disabled until it matches exactly. 5. Click **Count** to check the volume, or **Erase data** to erase, depending on step 3. Erasure cannot be undone Every trace of this subscriber in the application is permanently erased, including their tags, inbox messages, and journey state. Statistics stay aggregated. If the subscriber has a [custom User ID](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/), this removes every device linked to that User ID in this application, not only the device you opened it from. For the default identity (User ID same as HWID), only this device is erased. ![Erase personal data dialog with erasure warning, Count the volume first checkbox, and type-to-confirm field](https://docs.pushwoosh.com/userexplorer-13.webp) Erasing removes the subscriber from any journey they’re travelling through, which also cancels that journey’s next step for them. It doesn’t cancel a message scheduled or queued for them outside a journey, for example a delayed send. Each erasure appears as a row in the account-wide [Data requests](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) journal, alongside bulk CSV erasures. Count-only runs are hidden there by default. Turn on [Show volume counts](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/#show-volume-counts) to see them. Open a row to review the result or download a CSV report. The account can start up to 20 data erasure runs, from here or from **Data requests**, in a rolling 24 hours. ### Devices and platforms tab The **Devices and platforms** tab lists every channel linked to the user. Select a platform in the left sidebar to view its profile in the main panel. Note In Pushwoosh, email addresses are treated as devices, so they appear on the **Devices and platforms** tab along with mobile and other channels. [Learn more about how Pushwoosh handles devices](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/#understanding-how-pushwoosh-handles-devices) For each platform, you can review: * **Subscription status** (for example, **Subscribed** for email, or **Suppressed** if Pushwoosh blocked the address after a hard bounce or spam complaint). See [Reactivate a suppressed email address](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#reactivate-a-suppressed-email-address). * **Platform identifier** (for example, an email address or phone number) * **Metadata:** **Last active**, **First subscription**, **Location**, **Language**, and **Timezone** * **Tags** assigned to that platform. The table lists [default](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#default-tags) and [custom](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#custom-tags) tags that have values for this channel. You can edit values with the **pencil icon**. ![Devices and platforms tab with Email selected, showing subscription status, metadata, and platform tags](https://docs.pushwoosh.com/userexplorer-4.webp) #### Setting a test device With a channel selected in the sidebar, click **Set as test device** at the top of its profile on the right. In the dialog, enter a recognizable name, add a description if needed, and click **Apply**. ![Devices and platforms tab with Email profile and Set as test device button in the profile header](https://docs.pushwoosh.com/userexplorer-1.webp) The channel is added as a [test device](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/) and can receive test messages before you launch a campaign. To remove it, click **Remove from test devices** on that channel profile. #### Managing devices Use the **three-dot menu (⋮)** in the platform profile header to manage that channel. ##### Delete a device Select **Delete email** for email or **Delete device** for other platforms to remove the channel from the user profile. Warning Deleting a platform profile is irreversible and removes all associated data from the system. The profile will no longer receive messages or be linked to the user. ##### Unsubscribe user from marketing emails (email only) For an email profile, select **Unsubscribe from email campaigns** in the same menu to opt the user out of marketing email without removing the address. ![Email platform profile with three-dot menu showing Unsubscribe from email campaigns and Delete email options](https://docs.pushwoosh.com/userexplorer-2.webp) Pushwoosh sets the [**Unsubscribed Emails**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) tag to `true` for that address. The user will no longer receive marketing emails. Transactional emails are not affected and will still be delivered. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ##### Change an email address or phone number When a subscriber gets a new email address or phone number, change it on their existing channel instead of deleting the channel and adding a new one. The subscriber keeps their tags and subscription status. The option is available for email and SMS channels. Your role needs the **Modify applications** permission. See [role permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#edit-role-permissions). Without it, the option isn’t shown in the menu. 1. On the **Devices and platforms** tab, select the email or SMS channel in the left sidebar. 2. Open the **three-dot menu (⋮)** in the platform profile header and select **Change email…** or **Change phone number…**. ![Email platform profile with three-dot menu open and the Change email option highlighted](https://docs.pushwoosh.com/userexplorer-14.webp) 3. Enter the new address in the **New email** or **New phone number** field. For a phone number, use the international format with the country code, for example `+15551234567`. 4. Click **Change**. ![Change email dialog with the current address, a New email field, and Cancel and Change buttons](https://docs.pushwoosh.com/userexplorer-15.webp) After you click **Change**, Pushwoosh: * creates a new channel with the new address for the same user * copies the tags of the old channel to the new one * deletes the old channel Pushwoosh saves a phone number in E.164 format, without spaces or dashes: `+1 555 123-4567` becomes `+15551234567`. The change is refused, and the dialog shows an error, when the new address: * already belongs to another subscriber of this application * is an email address that Pushwoosh [suppressed](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#what-suppressed-means) after a hard bounce or a spam complaint * is an email address on a disposable email domain * isn’t a valid email address or phone number The [**Received messages**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#received-messages-tab) tab shows messages per channel, so messages sent to the old address no longer appear there: the new channel starts with an empty list. These messages still count in [message statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/). Each change is recorded on the [**Events history**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab) tab as the `PW_DeviceIdentityReplaced` event on the new channel. Its attributes are `old_hwid` (the previous address), `new_hwid` (the new address), and `platform`. Caution If your app or integration sends the old address to Pushwoosh again, for example through a sign-up form or an API call, Pushwoosh adds it back as a separate channel. Update the address in your own systems as well. #### Reactivate a suppressed email address **Reactivate** takes an address off the suppression list. Note Only the account owner can reactivate a suppressed address. If someone else needs to reactivate an address, contact Pushwoosh support. ##### What Suppressed means When Pushwoosh suppresses an email address for a hard bounce or a spam complaint, its **Subscription status** shows **Suppressed**, and Pushwoosh blocks all email to that address, including transactional. That is different from [unsubscribing from marketing emails](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#unsubscribe-user-from-marketing-emails-email-only), which only opts the user out of marketing email. ##### Review Email delivery status On the channel profile, open **Email delivery status** to see why the address was suppressed: * **Source:** **Spam complaint** or **Hard bounce** * **Date:** when the suppression occurred * **Reason:** a human-readable reason based on the mail provider’s feedback * **Recorded in:** the project where the suppression was logged. This is where the event was recorded, not which projects are blocked. Suppression applies across your whole account. * **SMTP and diagnostic codes:** additional detail from the mail server, shown when available. You don’t need these to reactivate. ##### How to reactivate If you’ve fixed the issue that caused the suppression, for example corrected the mailbox or the subscriber reconfirmed consent after a spam complaint, reactivate the address so Pushwoosh can send to it again: 1. On the **Devices and platforms** tab, select the email in the left sidebar. 2. Click **Reactivate**. 3. If the address was suppressed for a spam complaint, check the box confirming the recipient has renewed their consent. This is required for spam complaints, and the reactivation can’t proceed without it. A hard bounce reactivation skips this step. 4. Optionally, add a **Comment** (up to 255 characters), for example a reference to a support ticket or the subscriber’s own confirmation. 5. Click **Reactivate**. ![Reactivate email address dialog with a Comment field, Cancel button, and Reactivate button](https://docs.pushwoosh.com/emails-email-suppression-list-2.webp) Reactivating clears the [**Bounced Email**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) tag on the device. If a spam complaint had removed the email from this user’s **Devices and platforms** list, reactivation brings that email channel back onto the profile. Caution Reactivating doesn’t guarantee future deliverability. If the recipient files another spam complaint, Pushwoosh suppresses the address again. Reactivation applies to the whole Pushwoosh account, not just the project you’re viewing. The address becomes eligible for delivery again in every project under this account. Pushwoosh logs every reactivation, including who made it and when, so support can look it up if needed. To review or reactivate suppressed addresses across your whole account instead of one at a time, use the [Email suppression list](https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/). ### Events history tab Pushwoosh tracks and stores the events log for each user for up to one year. On the **Events history** tab, use **Search** to filter events by name. Use the channel dropdown to limit results to a specific platform. Events are listed in chronological order. Each row shows the timestamp, event name, and device or platform. ![Events History tab with search field, platform filter dropdown, and a table of user events](https://docs.pushwoosh.com/userexplorer-7.webp) Click the arrow at the start of a row to expand the event. The expanded section lists the attributes recorded for that event, such as platform, device type, or application version. Available attributes depend on the event type. ![Expanded Events History row showing event properties such as application_version and device_type for PW_ApplicationMinimized](https://docs.pushwoosh.com/userexplorer-8.webp) For example, `PW_ApplicationMinimized` may include `application_version` and `device_type`, and `PW_DeviceRegistered` may include `platform` and `platform_id`. See [default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) for system event names and typical attributes. ### Received messages tab The **Received messages** tab lists messages sent to this user across channels. Use **Search** to find a message by message code. Use the channel dropdown to show messages for one platform only. Use the **message type** selector next to the channel dropdown to narrow the list by how the message was sent: * **All messages:** show every message (default). * **Broadcast:** show only broadcast messages sent to a segment or your whole audience. * **Transactional:** show only [transactional](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) messages sent through the API to this specific user. ![Received Messages tab with search bar, Email channel filter, and a table of sent messages](https://docs.pushwoosh.com/userexplorer-9.webp) Each row includes: * **Message code / Source:** The message identifier and where it was sent from (for example, **Transaction** or **Customer Journey Builder**). Click the message code to open message details and statistics. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/). * **Sent:** The date and time the message was sent. * **Opened:** The date and time the user opened the message, if applicable. * **Content:** A preview of the message, such as the email subject line. Note If a message failed to send, the row shows **Failed to send**. Hover over the error icon to see why delivery failed. ### Inbox tab The **Inbox** tab shows what’s currently stored in this user’s [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/). This differs from the **Received messages** tab, which lists everything ever sent to the user’s devices. Note Pushwoosh tracks the inbox by user, not by device. If the same User ID is linked to several devices, they all show the same inbox. Use **Search** to filter messages by title or text. ![Inbox tab with search bar and a table of inbox messages showing Message, Status, Sent, and Expires columns](https://docs.pushwoosh.com/userexplorer-12.webp) Each row includes: * **Message:** The message title (or text preview, if there’s no title) and the platform it was sent to. * **Status:** The message’s inbox status: * **Not delivered:** the message hasn’t reached the user’s inbox yet. * **Unread:** delivered, but the user hasn’t opened it. * **Opened:** the user opened the message from the inbox. * **Read:** the user marked the message as read without opening it. * **Deleted by user:** the user removed the message from the inbox. * **Deleted:** the message was removed from the inbox by other means (for example, expiration cleanup). * **Sent:** The date and time the message was sent, or **––** if not available. * **Expires:** The expiration date and time, **Expired** if that date has passed, or **––** if the message has no expiration. ### Segments tab The **Segments** tab lists [segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) this user currently belongs to. ![Segments tab with search bar and a table of segment names and conditions](https://docs.pushwoosh.com/userexplorer-10.webp) Each row includes: * **Segment:** The segment name. Click the name to open the segment and view or edit its settings. * **Conditions:** The filter expression that defines the segment, including tags and other rules that match this user. ### Campaigns tab The **Campaigns** tab lists [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) campaigns this user has entered. Each row includes: * **Campaign:** The campaign name and type shown below it, such as **Audience based** or **Trigger based**. Click the name to open the campaign. * **Entry date:** The date and time the user entered the campaign. * **Status:** The user’s status in the campaign: * **IN CAMPAIGN:** the user is currently progressing through the journey. * **EXITED:** the user completed or left the journey. * **DROP-OFF:** the user was dropped from the journey due to an error. * **Leave date:** The date and time the user left the campaign. ![Campaigns tab with search bar and a table of campaign names, entry dates, status, and leave dates](https://docs.pushwoosh.com/userexplorer-11.webp) --- # Content creation and organization in Pushwoosh **URL:** https://docs.pushwoosh.com/product/content/ Understand how to design and manage content for push notifications, emails, and SMS while keeping it well-organized and reusable. --- Pushwoosh provides the tools and insights you need to create, manage, and optimize messaging content for your campaigns. Whether you are crafting a push notification, designing an email, creating web content, setting up rich media, or building SMS messages, Pushwoosh empowers you to deliver relevant and engaging communications across multiple channels. ## Available content types Content in Pushwoosh falls into three groups. ### Shared content tools Set these up once and use them across your content. [Brand book ](https://docs.pushwoosh.com/product/content/brand-book/) [Save your logo, colors, and fonts once, then apply consistent styling to any email or in-app template](https://docs.pushwoosh.com/product/content/brand-book/) [Media storage ](https://docs.pushwoosh.com/product/content/media-store/) [Upload and host images, then copy their URL to reuse them across content](https://docs.pushwoosh.com/product/content/media-store/) [Approvals and content versions ](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) [When approvals are on for your account, a reviewer must approve a push preset or email template before it can be sent. Compare versions and see their history](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) ### Content by channel Create content for the channel you send in. [Email content ](https://docs.pushwoosh.com/product/content/email-content/) [Create and reuse email content with the Drag & drop editor, the HTML code editor, AI tools, and templates](https://docs.pushwoosh.com/product/content/email-content/) [Push presets ](https://docs.pushwoosh.com/product/content/push-presets/) [Save push notifications with titles, messages, and media, and reuse them across campaigns](https://docs.pushwoosh.com/product/content/push-presets/) [In-app templates ](https://docs.pushwoosh.com/product/content/in-apps/) [Create in-app templates in the built-in editor or the native editor, or upload a ZIP file](https://docs.pushwoosh.com/product/content/in-apps/) [App Inbox presets ](https://docs.pushwoosh.com/product/content/app-inbox-presets/) [Create reusable App Inbox content with a layout, title, message, media, and custom data](https://docs.pushwoosh.com/product/content/app-inbox-presets/) [Web content ](https://docs.pushwoosh.com/product/content/web-popup-content/) [Create web content and web forms, and attach them to a web campaign](https://docs.pushwoosh.com/product/content/web-popup-content/) [Wallet passes ](https://docs.pushwoosh.com/product/content/wallet-passes/) [Design Apple Wallet and Google Wallet passes](https://docs.pushwoosh.com/product/content/wallet-passes/) [SMS presets ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) [Create reusable SMS content with personalization, several languages, and link shortening](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) [LINE presets ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) [Create LINE text, image, carousel, and Flex messages in several languages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) [Kakao templates ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) [Create KakaoTalk message templates with several types and personalization](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) [Rich messages ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) [Build one message with buttons and cards for RCS, WhatsApp, LINE, and Messenger, if enabled for your account](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) [Cloud Pages ](https://docs.pushwoosh.com/product/content/cloud-pages/) [Create Cloud Pages with forms, and use form submissions in journeys](https://docs.pushwoosh.com/product/content/cloud-pages/) ### Content sources Keep products, articles, and promo codes in one place and use them in messages. [Product catalog ](https://docs.pushwoosh.com/product/content/product-catalog/) [Keep product data in one place. Sync a feed, import a CSV, or add items by hand, then use them in Products blocks](https://docs.pushwoosh.com/product/content/product-catalog/) [News feeds ](https://docs.pushwoosh.com/product/content/news-feeds/) [Register an RSS or Atom feed to fill News blocks in email and News Feeds presets in SMS, or send new articles as push or inbox messages](https://docs.pushwoosh.com/product/content/news-feeds/) [Vouchers ](https://docs.pushwoosh.com/product/content/vouchers/) [Create voucher pools for messages with promo codes](https://docs.pushwoosh.com/product/content/vouchers/) ## Use Push AI composer and translator To streamline content creation, Pushwoosh offers the AI Content Composer, an AI-powered assistant that automatically generates high-quality, engaging copy for push notifications. Simply provide keywords, and the AI will suggest compelling messages tailored to your audience. This helps you save time, optimize messaging, and enhance engagement across all content types. [Learn more ](https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer/) ## Enhance email content with AI tools Pushwoosh’s Drag & drop editor includes built-in AI tools that help you create high-quality, personalized email content faster. [Learn more ](https://docs.pushwoosh.com/product/content/email-content/ai-tools-in-email-editor/) ## Content categories **Content categories** help you organize all types of content within a single project. These categories allow you to structure and manage content efficiently. This makes it easier to locate and reuse specific pieces of messaging material. ![Content categories](https://docs.pushwoosh.com/contentcategories.webp) Note Content categories apply to all content types (except push) within a project, helping organize messages across different channels. To set a category, click on **Default** in the **Category** column and then select or add a category. ![Set a category](https://docs.pushwoosh.com/contentcategories-1.webp) To remove a category from a piece of content, click the current category and select **Default** again to reset it back to uncategorized. --- # App Inbox presets **URL:** https://docs.pushwoosh.com/product/content/app-inbox-presets/ Create reusable App Inbox content with a layout, title, message, media, and custom data for one-time messages or journeys. --- **App Inbox presets** let you reuse the same inbox message in [one-time Inbox sends](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) and in the [App Inbox journey element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/), so you don’t recreate layout, copy, and media for every campaign. On the same page, you can split the app’s inbox into [tabs](https://docs.pushwoosh.com/product/content/app-inbox-presets/#set-up-inbox-tabs). Note The card layouts need Web SDK 3.75.0, Android SDK 6.12.0 with the `pushwoosh-inbox-ui` module, or iOS SDK 7.0.40 or later with the PushwooshInboxKit module, and the **Carousel** layout needs iOS SDK 7.1.2 or later. An older SDK still receives the message but shows it without the layout. ## Create an App Inbox preset 1. Go to **Content > Inbox content**. 2. Click **Create**. ![Inbox content list under Content with Create ready to start a new App Inbox preset](https://docs.pushwoosh.com/content-app-inbox-presets-3.webp) 3. Click the preset name next to **Inbox content:** at the top to rename it. ## Add languages You can create the same preset in multiple languages, the same way as [push presets](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push): 1. Compose the default-language content first. 2. Click the **+** icon next to **Default language**. 3. Select the languages you need. ## Choose a layout type In **Layout type**, choose how the card looks in the inbox list. Each option uses a different mix of media and text: * [**Classic**](https://docs.pushwoosh.com/product/content/app-inbox-presets/#classic): small icon beside **Title** and **Message**. No large image. * [**Captioned**](https://docs.pushwoosh.com/product/content/app-inbox-presets/#captioned): one large image with **Title** and **Message** under it. * [**Banner**](https://docs.pushwoosh.com/product/content/app-inbox-presets/#banner): full-width image only. No **Title** or **Message** in the list. * [**Carousel**](https://docs.pushwoosh.com/product/content/app-inbox-presets/#carousel): several images the user can swipe. One shared **Title** and **Message**, plus an optional caption on each slide. ![Layout type field set to Carousel in the App Inbox preset editor](https://docs.pushwoosh.com/content-app-inbox-presets-1.webp) ### Classic **Classic** is the compact list row: icon on the left, **Title** and **Message** next to it. Set **Title** and **Message** for the text in the inbox list. Set **Icon URL** to a URL, or pick an image from [Media storage](https://docs.pushwoosh.com/product/content/media-store/). Use a 1:1 aspect ratio when you can. ![Classic App Inbox layout preview with icon, title, and message in the inbox list](https://docs.pushwoosh.com/content-app-inbox-presets-4.webp) ### Captioned **Captioned** is a single hero image with copy under it. Set **Title** and **Message**. Set **Image URL** to a URL, or pick an image from Media storage. Use a 1:2 aspect ratio when you can. ![Captioned App Inbox layout preview with large image, title, and message underneath](https://docs.pushwoosh.com/content-app-inbox-presets-5.webp) ### Banner **Banner** is image-only: the inbox list shows a full-width image and no text fields. Set **Image URL** only (URL or Media storage). Use a 1:2 aspect ratio when you can. ![Banner App Inbox layout preview showing a full-width image with no title or message](https://docs.pushwoosh.com/content-app-inbox-presets-6.webp) ### Carousel **Carousel** is a multi-image card. The user swipes between slides. **Title** and **Message** apply to the whole card. Each slide can also have its own **Caption**. Set **Title** and **Message**, then in **Media** set each slide’s **Image URL**. Optionally set a **Caption** on each slide. Click **Add image** to add another slide. ![Carousel preset editor with two slides, Image URL and Caption fields, Add image, and inbox list preview](https://docs.pushwoosh.com/content-app-inbox-presets-2.webp) Note Across every layout, use JPG, JPEG, or PNG for images. GIF isn’t supported. ## Validate Liquid syntax If you use [Liquid templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) in the message, click **Validate Liquid Syntax** to check for syntax errors before saving. ## Add custom data Use custom data when the app needs more than the visible inbox text, for example to open a screen, pass a campaign id, or run logic after the user opens the message. Pushwoosh sends this JSON to the SDK with the message, the same as [Custom Data in push presets](https://docs.pushwoosh.com/product/content/push-presets/#add-custom-data). Click **Insert JSON** and enter a JSON object with key-value pairs. ## Preview Check the preview labeled **App inbox** (with an unread count) to see how the message looks in the inbox list. Switch the **iOS**, **Android**, and **Web** tabs to check each platform. ### Send a test message To check the message on a real device before you use the preset, send it to your [test devices](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/). Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, **Test app inbox message** is hidden. 1. Click **Test app inbox message** under the preview. 2. In **Test app inbox content**, check how many devices of the app are marked for testing. 3. Click **Send**. The test goes to every test device of the app, whichever platform tab is selected in the preview. It uses the content as it is in the editor, so you don’t need to save the preset first. The test message has no on-click action, because actions are set when you send the message. ## Save Click **Save** next to the preset name to store the preset. Saved presets appear under **Content > Inbox content**. Use them in [one-time Inbox messages](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) and the [App Inbox journey element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/). ## Delete presets To delete several presets at once: 1. Go to **Content > Inbox content**. 2. Click **Bulk actions** → **Delete…**. 3. Select the presets. 4. Click the **Delete** button above the list. Presets used in journeys are skipped. Deleted presets can’t be restored. ## Set up inbox tabs Inbox tabs split the app’s inbox into up to three topics, such as **Offers** and **News**. A message sent to a tab shows in that tab and in **All**. A message sent with no tab shows only in **All**. Until you add a tab, the inbox is one list. Note Tabs show in the inbox of iOS SDK 7.3.0 and Android SDK 6.17.0 or later. The Web SDK inbox has no tabs. ![Inbox tabs page with the All tab marked Shows all messages, the Offers and News tabs, and Add tab](https://docs.pushwoosh.com/content-app-inbox-presets-7.webp) ### Add a tab 1. Go to **Content > Inbox content**. 2. Click **Inbox tabs**. 3. Click **Add tab**. 4. In **Title**, enter the tab name, up to 32 characters. 5. To translate the name, add languages in the language selector above **Title** and enter the name for each. If a user’s language has no translation, the user sees the **Default** name. 6. Click **Save**. The first tab you add also creates **All**, marked **Shows all messages**. **All** stays first, and you can edit its title but not move or delete it. **Add tab** turns inactive once the app has three tabs besides **All**. To pick a tab for a message, see [one-time Inbox messages](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/#choose-a-tab) and the [App Inbox journey element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/#choose-a-tab). ### Change, reorder, or delete tabs * To rename a tab or change its translations, open the actions menu (⋮) on its row and click **Edit**. * To change the order, click the up or down arrow on a tab’s row. * To delete a tab, open the actions menu (⋮) on its row, click **Delete…**, and confirm. Messages already in the tab stay in the inbox and show only in **All**. A deleted tab can’t be restored. Caution Before you delete a tab, choose another tab in every journey whose App Inbox element uses it. Pushwoosh doesn’t check whether a tab is in use, and an App Inbox element set to a deleted tab can’t send its message. When you delete the last tab besides **All**, **All** is deleted too, and the inbox is one list again. --- # Approvals and content versions **URL:** https://docs.pushwoosh.com/product/content/approvals-and-content-versions/ Review push preset and email template versions before sending, with version history, comparison, and an approval review queue. --- Every time you save a push preset or email template, Pushwoosh keeps a new version of it. That works for every account. Content approval is an optional extra step. When it’s on for your account, you can’t send a version until a reviewer approves it. You can always [view, compare, and restore versions](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/#view-compare-and-restore-versions), whether or not content approval is on. Note Versioning and approval cover push presets and email templates only. SMS, in-app, LINE, and WhatsApp content have no version history and aren’t affected by this restriction. ## Turn on content approval By default, every saved version is approved automatically. Content keeps sending as before. To require a review before sending, ask Pushwoosh support to turn on the **Require content approval** restriction for your account. ## Grant the Approve messages permission Reviewers need the **Approve messages** permission to open the review queue and approve or reject submitted versions. 1. Open **Users and roles**. 2. Add **Approve messages** to a role. It sits in the **Messages** group with **Send messages** and **Delete messages**. If you don’t manage roles, ask an account admin to add **Approve messages** for reviewers. See [Available permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#available-permissions) for the full roles matrix. ## Submit content for approval Once content approval is on, both editors show a version status (**Draft**, **On approval**, **Approved**, or **Rejected**) and a **Submit for approval** button. Where they sit differs for push and email. ### Push presets In the [push content editor](https://docs.pushwoosh.com/product/content/push-presets/), **Version history** sits next to **Save**. The version status and **Submit for approval** sit together on their own row below. ![Push content editor showing the Draft version status next to a Submit for approval button, with Version history in the toolbar above](https://docs.pushwoosh.com/approvals-and-content-versions-1.webp) To submit a version for review, click **Submit for approval**. The status changes to **On approval**, and the version appears in the reviewer’s queue. **Submit for approval** becomes unavailable. **Version history** stays available. ![Push content editor On approval status next to disabled Submit for approval, with Version history still available in the toolbar above](https://docs.pushwoosh.com/approvals-and-content-versions-2.webp) ### Email templates In the [email content editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) (including its AI tab), the version status appears next to the email title. ![Email content editor showing the Draft version status next to a Submit for approval button](https://docs.pushwoosh.com/approvals-and-content-versions-7.webp) To submit a version for review, click **Submit for approval**. The status changes to **On approval**, and the version appears in the reviewer’s queue. **Submit for approval** becomes unavailable while the version is on approval. ![Email content editor showing the On approval version status, with Submit for approval disabled](https://docs.pushwoosh.com/approvals-and-content-versions-8.webp) Note A reviewer can’t approve or reject their own submission. Have a different user with the **Approve messages** permission review it. If a reviewer rejects it, the status changes to **Rejected** and shows the reviewer’s reason on its second line. Edit the content and click **Submit for approval** again to resubmit. ### What happens when you edit approved content If you edit a push preset or email template that is **Approved**, its status changes to **Draft** and shows a new version number, for example **Draft · v4**. Any request still waiting for approval on that content is cancelled. ## Review pending approvals Open **Content**, choose **Push Presets** or **Email content**, then open the **Pending approval** tab. The number next to that item in the left menu is how many versions are waiting. What the queue includes depends on the channel: * **Push presets:** versions waiting in the project you have open now. Versions from other projects don’t appear here. ![Pending approval tab with a submitted version and Compare, Reject, and Approve buttons](https://docs.pushwoosh.com/approvals-and-content-versions-3.webp) * **Email content:** versions waiting in every project in the account. Email cards also include **Open template**. ![Email content Pending approval tab with a submitted template and Compare, Open template, Reject, and Approve buttons](https://docs.pushwoosh.com/approvals-and-content-versions-10.webp) Each pending card has the actions below. ### Compare Click **Compare** to open a side-by-side view of the previous version and this one. Use it to check what changed before you approve or reject. ![Compare dialog showing the previous version and this version side by side](https://docs.pushwoosh.com/approvals-and-content-versions-4.webp) ### Open template On email cards only, click **Open template** to open that template in the email content editor. Use it when you need the full editor view, not only the card preview. ### Reject Click **Reject** to decline the version. A reason is required. The reason appears on the card in the list and on the version status in the editor, so the editor can see why it was rejected and resubmit after fixing it. ![Reject dialog with a required reason text field and Cancel and Reject buttons at the bottom](https://docs.pushwoosh.com/approvals-and-content-versions-5.webp) ### Approve Click **Approve** to accept the version. Its status becomes **Approved**, it leaves the pending queue, and it can be sent. Note Submitting a version doesn’t send the editor or reviewer an email. The pending-approval queue and the menu badge are the only places the request is visible, so check them directly rather than waiting for a notification. ## What happens when you try to send unapproved content With content approval on, only an **Approved** version can be sent. Anything still in **Draft**, **On approval**, or **Rejected** is blocked. * **One-time push and one-time email.** Unapproved content still appears in the content dropdown, but you can’t select it. The one-time email form behaves the same way. ![One-time push form Content step showing an On approval preset greyed out and unselectable in the dropdown](https://docs.pushwoosh.com/approvals-and-content-versions-6.webp) * **Customer Journey.** In the **Send Push** and **Send Email** journey elements, the content dropdown shows the same version status next to each option, and any option that isn’t **Approved** is disabled. If a previously picked preset or template stops being **Approved** (for example, a later edit created a new **Draft** version), a note appears under the dropdown: “This content is not approved and cannot be sent.” Launching or resuming a Journey also checks every push preset and email template it would send. If any of them isn’t **Approved**, the [pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) flags the point with `Content is not approved and cannot be sent`. The Journey can’t launch or resume until you fix it or pick approved content. This only catches content that’s already unapproved before launch or resume. If content stops being **Approved** while the Journey is already running, the traveler gets a [**Content not approved**](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#content-not-approved) drop-off reason instead of a generic internal error. A **Send Push** element also pauses the Journey. **Send Email** only drops that traveler and keeps the Journey running. If you call the API directly, [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) (push) or [`createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage) (email) rejects an unapproved version with error code `210`. A preset-based send gets the message `Preset content is not approved`. A transactional email sent by template code only, with no preset, gets `Email Template content is not approved` instead. ## Creating content for a one-time send You can still create push presets and email templates. With content approval on, you just can’t create them from the one-time push or one-time email form. Instead: 1. Create the preset or template under **Content** → **Push presets** or **Content** → **Email content**. 2. Submit it for approval and wait until its status is **Approved**. 3. Open the one-time form and select that approved content from the dropdown. ## Testing content With content approval on, [**Test push notification**](https://docs.pushwoosh.com/product/content/push-presets/#preview-and-save-push-content) is hidden in the push content editor, and **Test email** is hidden in the email content editor. This applies for every version status. ## View, compare, and restore versions Every push preset and email template keeps its last 20 versions in its version history. You can open this history whether or not content approval is on for your account. A version waiting for approval stays in history even if you already have 20 other versions. It is not dropped to make room for newer ones. From the [push content editor](https://docs.pushwoosh.com/product/content/push-presets/) or the [email content editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#restore-a-previous-version), open **Version history** next to **Save** to: * **Compare** two versions side by side to see what changed between them. * **Restore** an earlier version. Nothing is saved until you click **Save**. * **Push preset:** restores the version’s text. Media, actions, and settings stay as they are in the editor. * **Email template:** restores the HTML content, locale variants, and theme. It does not restore the email-wide styling and footer from the **Settings** tab. Note Push presets and email templates that already existed when content approval was turned on for the account got a **v1** version created for them retroactively, already approved. --- # Create a Brand book **URL:** https://docs.pushwoosh.com/product/content/brand-book/ Save your logo, colors, fonts, block defaults, and brand voice as a Brand book, then apply them to any template in one click. --- Brand book saves your logo, colors, fonts, block defaults, and brand voice once, so you can apply consistent styling to any email or in-app template instead of setting it up from scratch each time. Note Pushwoosh keeps one Brand book per application. Opening Brand book again lets you edit and re-save it. ## Open Brand book Open Brand book from any of these places: * **Content → Email Content**: click **Brand book** in the header, next to **How to use**. Brand book is also offered on the empty list screen, before you’ve created any email content. This opens Brand book directly, without opening or creating an email template. * [Email Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/): open the **⋮** menu and select **Brand book**, or click the **Brand book** button pinned to the bottom of the **Blocks** or **Settings** tab. * [Built-in in-app editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/): open the **⋮** menu and select **Brand book**. All these open the same Brand book for the current application, so it doesn’t matter which one you use. Another application in your account keeps its own separate Brand book. ![Content → Email Content list header with the Brand book button highlighted next to How to use](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-99.webp) ## Accept a brand kit suggested from your email history Pushwoosh can build a brand kit for you automatically, from the templates and messages your app has already sent, and offer it as a ready-made starting point instead of a blank Brand book. Once an app has sent enough email templates and messages, Pushwoosh scans them for a shared style: colors, fonts, logo, and tone of voice. How you see the suggestion depends on where you open Brand book. ### Save the suggestion in an editor 1. Open the email, in-app, or cloud page editor. The suggestion opens as a popup and shows how many templates and messages it’s based on. 2. Check the suggested colors, fonts, logo, and tone of voice. 3. Choose how to save it: * **Save to Brand book** saves the suggestion as is. * **Review & edit** opens Brand book with the suggestion filled into the form, so you can adjust it before saving. Nothing is saved until you click **Save to Brand book** in the popup or **Save brand** in the form. To decide later, [postpone the suggestion](https://docs.pushwoosh.com/product/content/brand-book/#postpone-the-suggestion). #### When the logo can’t be picked automatically If the popup shows a **logo not found** label, Pushwoosh couldn’t pick one logo with confidence. It shows up to three candidate images from your emails instead: * Pick one to carry it into the brand kit. * Leave the selection empty to save the kit with your current Brand book logo unchanged. You can also set the logo manually afterwards in the [Logo](https://docs.pushwoosh.com/product/content/brand-book/#logo) section below. #### When there isn’t enough voice data If the popup shows a **Tone of voice was skipped** banner, your app hasn’t sent enough email copy to set a reliable tone, so **Tone of voice** stays empty. Fill in [Brand voice](https://docs.pushwoosh.com/product/content/brand-book/#brand-voice) yourself after you save the kit, or wait until the app sends more email. #### Postpone the suggestion To decide later, click **Not now**, press **Esc**, or click outside the popup. Nothing is applied, and the popup doesn’t open again by itself. When you’re ready, [open Brand book](https://docs.pushwoosh.com/product/content/brand-book/#open-brand-book). The suggestion waits there as a banner marked as waiting. Review and save it from the banner. ### Save the suggestion from the Email Content list 1. Open Brand book from **Content → Email Content**. The suggestion appears as a banner inside the panel. 2. Click **Review** to fill the form with the suggestion. 3. Adjust the fields if needed, then click **Save brand**. **Review** fills two fields differently: * **Logo:** filled with its best-guess candidate only if you haven’t set a logo yet. An existing logo stays untouched. Set your own in [Logo](https://docs.pushwoosh.com/product/content/brand-book/#logo) below if you need a different one. * **Brand voice:** replaced with the suggestion’s value whenever the suggestion has one, even if you’d already set your own. If the suggestion has no voice, **Brand voice** stays as it is, with no separate notice. If it stays empty, fill it in yourself in [Brand voice](https://docs.pushwoosh.com/product/content/brand-book/#brand-voice). Click **×** to dismiss the banner instead. If you close the panel without clicking either one, the banner stays for next time, the same as when you [postpone](https://docs.pushwoosh.com/product/content/brand-book/#postpone-the-suggestion) a popup suggestion. Note Pushwoosh builds this kit per application, not per account. An app whose sent templates only use another app’s branding doesn’t get a kit. This suggestion and [Build from your website](https://docs.pushwoosh.com/product/content/brand-book/#build-from-your-website) below both fill the same logo, color, font, and Brand voice fields from a different source. Whichever one you save last is what Brand book keeps. ## Set up your brand Define how your brand looks and sounds across templates: logo, colors, fonts, block defaults, and brand voice. Scan your website to fill most fields automatically, or set them yourself in the sections below. ### Build from your website Scan your site to fill most brand fields automatically. 1. Enter your domain in the **Build from your website** field. 2. Click **Scan site**. Pushwoosh reads the page and its stylesheets for colors, fonts, logo, and the tone of its copy. It always fills the palette and fonts below, and also fills the logo and **Brand voice** when the site provides them. Nothing is saved until you click **Save brand**. ![Build from your website section after a successful scan, showing detected colors, fonts, logo, and brand voice](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-65.webp) Important If the scan fails because the domain is unreachable, times out, or blocks the request, Pushwoosh shows the error below **Derive brand** and leaves your current settings untouched. Fix the URL and scan again, or use **Derive brand** with a description, file, or your logo instead. ### Brand voice Describe how your brand talks to customers, either by writing it yourself or by having Pushwoosh derive it from a file. * **Write it yourself:** enter your brand voice in the **Brand voice** field. A site scan fills this in automatically when the site provides it. * **Derive it from a file:** click **Attach files** to attach one file (an image, a brand-book PDF, or a web or text file), then click **Derive brand** to generate a palette, fonts, and voice from it. Your current logo is used automatically as a source if you don’t attach an image. ![Brand voice section with text field, attached file, and Attach files and Derive brand buttons](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-66.webp) Note Saved Brand voice steers your brand’s tone when AI generates a new email or in-app message from scratch. It doesn’t carry over when you ask the AI assistant to edit content you’ve already generated. ### Logo If you scanned your site above, **Logo** may already be filled. Use the steps below to add or change it manually. 1. Click **Choose logo**. 2. Pick a logo from your [Media storage](https://docs.pushwoosh.com/product/content/media-store/), or paste a hosted image URL. ![Logo section with preview, Change logo and Remove buttons, and hosted image URL field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-67.webp) The [Logo header](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#logo-header) block in the email editor starts from this logo. To set how that header looks by default, open the **Logo header** accordion below **Logo**: ![Brand book Logo accordion expanded, showing the Logo header sub-section with Dark mode image, Alignment, and Width fields](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-95.webp) * **Dark mode image:** shown by Apple Mail and Outlook in dark mode. Gmail keeps the main image. * **Alignment:** left, center, or right. * **Width:** the logo’s width in px. * **URL:** a destination to make the logo clickable. * **Background:** turn on to set a background color behind the logo. * **Padding:** turn on to add space around the logo. Use **All sides** for uniform padding, or turn on **More options** to set each side separately. * **Bottom border:** turn on to draw a line under the header, then set its **Width** and color. How these defaults reach your emails: * Every Logo header block in the email starts from this look and picks up your changes here the next time you [apply the brand](https://docs.pushwoosh.com/product/content/brand-book/#apply-or-reset-brand-styles). * A header you edited directly on the canvas keeps its own look instead, until you reset it. * To write a canvas edit back here, use **Save to Brand book** on that header. See [Logo header](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#logo-header). * The email footer logo comes from here too. If **Logo** is set, applying the brand sets the footer logo to **Logo** and the footer’s dark mode image to **Dark mode image** above. If **Dark mode image** is empty, the footer shows the main logo in dark mode as well. Other footer fields stay as they are. See [Configure the email footer](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/#email-type-and-company-info). ### Colors, fonts, and block defaults If you scanned your site above, the palette and fonts may already be filled. Fine-tune colors, fonts, and block defaults in the accordion sections below **Logo** (the same styling options as the email editor’s **Settings** tab). [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/#configure-email-wide-styling) ## Save and apply your brand Click **Save brand** to store your settings. ![Brand book panel with the Save brand button highlighted, next to a live preview of an email using the brand style](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-68.webp) If you opened Brand book from an email or in-app template you were editing, you’re asked to apply the brand to it: * **Apply:** repaints colors and replaces fonts and block defaults with your saved brand. The content itself is not affected. * **Not now:** saves the brand for later without changing what you’re editing. If you opened Brand book from the Email Content list instead, there’s no template open to apply the brand to. **Save brand** stores your settings and closes the panel. You can apply your saved brand to any other email or in-app template the same way. Open Brand book from it and save again. Note Saving Brand book only updates the Brand book itself. Templates you already created don’t pick up the change on their own. Open each one and apply the updated brand, either here or with the options below. ## Apply or reset brand styles Use these options to bring a template you already created up to date with your latest saved brand. While you’re editing an email or in-app template, open the **⋮** menu and use the options under **Brand styles**: * **Apply brand book styles:** apply your saved brand to the template without opening Brand book. Unavailable until you’ve saved a brand at least once. * **Reset custom styles:** remove style overrides so colors, fonts, and spacing follow the Brand book again. Content and layout stay the same. ![In-app editor kebab menu with Preview, Brand book, Apply brand book styles, and Reset custom styles](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-59.webp) In the email Drag & drop editor, the same two actions are also one click away: click the **⋮** next to the **Brand book** button pinned to the bottom of the **Blocks** or **Settings** tab. ![Pinned footer at the bottom of the Blocks tab with a Brand book button and a kebab menu open, showing Apply brand book styles and Reset custom styles](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-97.webp) Confirm each action when prompted. Use **Undo** in the top bar to reverse it. --- # Cloud Pages overview **URL:** https://docs.pushwoosh.com/product/content/cloud-pages/ Create and publish Cloud Pages and forms on your domain, capture leads in Pushwoosh, and use form submissions in journeys. --- Use Cloud Pages for promo campaigns, lead capture forms, quizzes, and click-through pages. Build pages in the visual editor, publish them, and track form submissions to trigger journeys. Note Cloud Pages is available on request. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to enable it for your project. ## When to use Cloud Pages Use Cloud Pages when you need campaign-specific web content and want to route users to the next action. * **Promo pages:** Send users from email or push campaigns to a dedicated page on your domain. * **Lead and subscription forms:** Collect email addresses and phone numbers to build Pushwoosh audiences. * **Campaign pages and quizzes:** Launch a simple page with a CTA, fields, or a short quiz. ## How to build and manage pages You can build and manage Cloud Pages in a drag-and-drop editor: * Add and rearrange content blocks with drag-and-drop. * Use HTML blocks for full layout control. * Generate content blocks with AI Smart Blocks. * Search, sort, and manage pages from the page list. To create and publish your first Cloud Page, follow this step-by-step guide: [Create a Cloud Page ](https://docs.pushwoosh.com/product/content/cloud-pages/create-and-publish-a-cloud-page/) ## How to publish pages Every published page gets a public URL automatically: ```text https://cloud-pages.svc-nue.pushwoosh.com/cloud-pages/v1/page/ ``` Tip Custom domain publishing is configured by request through [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). The page becomes available at `https://yourdomain.com/{slug}` only after the support team completes the setup. ## How to track form submissions If your page includes a form, Pushwoosh fires `PW_FormSubmitted` on submission and saves submitted values to the user profile and tags, including `PW_CloudPageCode`. You can use `PW_FormSubmitted` as a trigger in Customer Journey. [Learn more](https://docs.pushwoosh.com/product/content/cloud-pages/create-and-publish-a-cloud-page/). --- # Create a Cloud Page **URL:** https://docs.pushwoosh.com/product/content/cloud-pages/create-and-publish-a-cloud-page/ Create a Cloud Page in Pushwoosh, add a lead form, publish it with a public URL, and verify form submission and redirect flow. --- Use this guide to create and publish a Cloud Page. A Cloud Page is a public web page you build in Pushwoosh for promo campaigns, lead forms, quizzes, and other click-through landing pages. ## Before you start * Cloud Pages is available on request. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to enable it for your project. * Decide where to redirect users after the main action on the page. The redirect can be any URL you control, for example a thank-you page, a results page, another Cloud Page, or another campaign URL. Prepare that URL before you test submit. ## Create a page 1. Go to **Content** → **Cloud Pages**. 2. Click **Create cloud page**. This opens the page editor directly, where name, redirect, and content all live on one screen. ![Cloud Pages list view with Create cloud page button highlighted by a red arrow](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-1.webp) 3. Click the page name next to **Create page:** and enter a name. 4. Add the URL in **After submit, redirect to**. This field is required. Use it to define where users go after completing an action on the page, for example to a thank-you page or another campaign page. ![Cloud Page editor top bar with page name, After submit redirect to URL field, and existing-user dropdown](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-2.webp) 5. In the dropdown next to it, choose what happens when an existing contact submits the form again: * **Update existing user** (default): updates the contact profile fields with the submitted values. * **Skip tags for existing user**: registers the device the user submits from, but does not update existing tags on the contact profile. * **Show error for existing user**: for one-time signups such as exclusive offers or webinars. If the contact already exists, the submission is rejected, the profile is not updated, and the user is redirected back to the form and sees an error message. ## Build page content Most lead pages need a **Form** block. Follow [Create a lead or submission form](https://docs.pushwoosh.com/product/content/cloud-pages/create-and-publish-a-cloud-page/#create-a-lead-or-submission-form) after you add the basic layout. If you only need content and a button, you can skip the form section. 1. In the **Blocks** panel on the right, click or drag blocks onto the canvas. * Most blocks are the same as in the email editor. To configure them, see the [email editor blocks](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/) guide. * **Group:** drop blocks into a Group when you want to move, hide, or style several of them together (shared background and spacing), or arrange them in a row instead of stacked with **Direction: Horizontal**. Add or remove blocks in the **Layers** panel by dragging them into or out of the group. * **Form:** see [Create a lead or submission form](https://docs.pushwoosh.com/product/content/cloud-pages/create-and-publish-a-cloud-page/#create-a-lead-or-submission-form) below. 2. Edit the block content on the canvas. 3. Repeat for each block you need. 4. Click **Save**. ![Cloud Page editor with Blocks panel open showing Heading through column layouts and an empty canvas](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-3.webp) Tip The **AI** tab next to **Blocks** can generate a starting layout from a text prompt or a suggested template (newsletter signup, event registration, product launch) instead of building it block by block. ## Create a lead or submission form Add a form to collect contact details and other values you need for segmentation. A **Tag name** is the field name saved on the contact. Use a clear name such as `email` or `fitness_goal` so you can find that value later in segments and User Explorer. 1. From **Blocks**, add a **Form** block. 2. In the **Form** panel, under **Fields**, click **Add field**. ![Form panel Fields list with Email required field and Add field button](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-12.webp) 3. In the fields list, click the edit (pencil) icon on the field to open its settings. 4. In the field settings, configure the field: * **Tag name:** the name saved on the contact (for example `fitness_goal`) * **Type:** the input type (for example Text, Phone, or Dropdown) * **Label** and **Placeholder** * **Options (one per line):** if **Type** is **Dropdown**, enter one choice per line * **Required:** enable if users must fill this field before submit To delete the field, click **Remove field**. ![Form field settings for a Dropdown with Tag name fitness_goal, Label Fitness goal, Options list, and Required enabled](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-9.webp) 5. Repeat steps 2–4 for each field you need. 6. Under **Submit button**, set the **Label**. Optionally adjust **Background**, **Text color**, and **Corner radius**. ![Submit button settings with Label Get My Plan, Background Solid color, Text color, and Corner radius](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-11.webp) 7. Click **Save**. ### What happens after form submission After submit, Pushwoosh creates or updates the contact and sends the user to your redirect URL. The details below help you verify the result in User Explorer and use the submission in Customer Journey. They are not extra setup steps in the editor. Note The flow below describes what happens when a new contact submits the form. For existing contacts, the outcome depends on the **On existing user** page setting. #### Contact profile and devices When a user submits the form, Pushwoosh creates or updates a contact profile in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). For example, if the form includes an email field and a phone field, Pushwoosh registers one Email device and one SMS device. Both devices are linked under one [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). ![Contact profile in User Explorer showing Email and SMS devices registered after form submission](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-6.webp) #### Tags All other fields are saved as custom tags by field name. For example, if your form has `fitness_goal` and `workout_frequency` fields, the contact profile will show tags like `fitness_goal = Boost endurance` and `workout_frequency = 3–4x a week`. The page code is saved in the `PW_CloudPageCode` tag. ![Contact profile in User Explorer showing custom tags saved after form submission](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-8.webp) #### Event Pushwoosh fires `PW_FormSubmitted` on every form submission. You can see it in the **Events History** tab on the contact profile in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). The event includes a `form_code` attribute that identifies which Cloud Page the submission came from, and a timestamp of when it occurred. ![User Explorer Events History tab with PW_FormSubmitted expanded to show form_code attribute](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-5.webp) Use it as a trigger in Customer Journey to start welcome sequences, follow-up flows, and other automation. For example, you can build a journey that sends a welcome email immediately after a user submits a fitness quiz form, then follows up with a personalized workout plan 24 hours later. ## Test the page After you save the page, it becomes publicly accessible via its URL. The default format is `https://cloud-pages.svc-nue.pushwoosh.com/cloud-pages/v1/page/`. Note If you requested custom domain setup through [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) and the setup is complete, it will use your domain URL instead: `https://domain.com/{slug}`. 1. Use the back control in the editor to return to **Content** → **Cloud Pages**. 2. In the page list, open the page action menu and click **Preview**. 3. Copy the public URL from the browser address bar when the preview opens. Use that URL in a message CTA, Customer Journey, or ad. 4. Verify that the page content loads correctly and is displayed as expected. 5. If your page includes a form, fill in the form with test data and click the submit button. Use a clearly named test email so you can find or remove that contact in User Explorer afterward. Note Test submissions create real contact profiles in Pushwoosh. 6. Confirm that the redirect opens the expected page. 7. Open [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) and confirm that the submitted user data and tags are displayed. ## Edit or delete a page All Cloud Pages are listed in **Content** → **Cloud Pages**, where you can manage and edit them. 1. Go to **Content** → **Cloud Pages**. 2. Open the page action menu. 3. Click **Edit** or **Delete** the page. ![Cloud Pages list with page action menu open showing Edit, Preview, and Delete](https://docs.pushwoosh.com/cloud-pages-create-and-publish-a-cloud-page-7.webp) --- # How to create and manage email content **URL:** https://docs.pushwoosh.com/product/content/email-content/ Learn how to effortlessly create, manage and reuse email content with Pushwoosh. Use Drag & drop editor, HTML code editor, AI tools, and templates for professional campaigns. --- ## Create new email content > **Important:** Before you begin creating email content, make sure that you set up your email. The setup process is the same for both the Drag & drop editor and HTML code editor. It includes configuring sender details, subject lines, and language versions. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/) Pushwoosh provides several flexible options for creating email content, so you can choose the method that best fits your skills and needs: #### Drag & drop editor A no-code, visual builder that lets you create professional emails using content blocks and ready-made layouts. Perfect for marketers and non-technical users. [Create email content with Drag & drop editor ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) #### Apply consistent branding Save your logo, colors, fonts, and block defaults once as a Brand book, then apply them to any email in one click instead of styling each one from scratch. [Apply consistent branding with Brand book ](https://docs.pushwoosh.com/product/content/brand-book/) #### HTML code editor Gives you full control over the design and layout using HTML. Ideal for users comfortable with coding. [Create email content with HTML Code Editor ](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/) #### AI-powered tools Integrated into the Drag & drop editor, AI tools help you quickly generate text, improve copy, and create images. [Use AI tools in the Drag & drop editor ](https://docs.pushwoosh.com/product/content/email-content/ai-tools-in-email-editor/) #### Pre-built email templates Choose from professionally designed templates to save time and maintain consistency across campaigns. [Browse email templates ](https://docs.pushwoosh.com/product/content/email-content/email-templates-library/) #### Reuse email content across campaigns Insert one piece of email content into another to reuse headers, footers, or other blocks without rebuilding them. [Reuse email content blocks across campaigns ](https://docs.pushwoosh.com/product/content/email-content/reuse-email-content-blocks/) You can access all these options from **Content → Email Content** or while setting up your campaign. Tip Watch this video to learn how to create email content with the Drag & drop editor or HTML code editor. [Youtube Video: How to create email content using the Drag & drop editor or HTML code editor](https://youtube.com/watch?v=vsfbEl4Y588) Pushwoosh automatically saves your previously created email content, making it easy to edit, clone, or reuse in future campaigns. #### Ad inventory Create and manage the sponsored campaigns and creatives your account sells through email Ad slots and Sponsored push. Open it from **Content → Ad inventory**. [Ad inventory ](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) ## View and manage your email content To view your email content, navigate to **Content > Email Content**. Here, you’ll find all the email content you’ve created, conveniently organized for easy access. Note Pushwoosh keeps your email content application-specific, making it well-organized and simple to manage. You have the flexibility to view your email content in two ways: as tiles or as a list. Simply select the preferred option at the top of the content list. ![Email content list view showing tiles and list view options](https://docs.pushwoosh.com/content-email-content-1.webp) Here, you can also edit, clone, delete email content, send an email or create new email content. ![Email content management options showing edit, clone, delete, and send actions](https://docs.pushwoosh.com/content-email-content-2.webp) To delete several emails at once: 1. Click **Bulk actions** → **Delete…**. 2. Select the emails. 3. Click the **Delete** button above the list. Emails used in journeys, double opt-in settings, subscription forms, or emails not sent yet are skipped. Deleting a single email in use fails too, and the dialog lists where the email is used. Emails already sent aren’t affected, but API sends that use the deleted content fail. Deletion can’t be undone. Also, you have the option to search for specific emails or filter your content based on the labels you’ve assigned. To do this, click on **Search by label…** at the top of the content list and start typing to find the label you need. ![Email content filtering by labels dropdown menu](https://docs.pushwoosh.com/content-email-content-3.webp) To assign a label, click **Default** in the label column and select an existing label or add a new one. Select **Default** again to remove the label. ## Review pending approvals If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, each email in the list shows a version status chip (**Draft**, **On approval**, **Approved**, or **Rejected**), and a **Pending approval** tab lists templates waiting for review. A badge on the **Content** → **Email Content** menu item shows how many versions are waiting. Note Unlike the push presets queue, which only covers the current project, the email templates **Pending approval** tab lists versions from every project in the account. A user with the **Approve messages** permission can compare a pending version against the previous one, approve it, or reject it with a reason. The reason appears both on the template’s card in the list and on its status chip in the editor. ![Email content Pending approval tab with a submitted template and Compare, Open template, Reject, and Approve buttons](https://docs.pushwoosh.com/approvals-and-content-versions-10.webp) See [Approvals and content versions](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) for the full workflow. ## Reuse previously created email content To streamline your workflow, leverage your existing email content in new campaigns. For this, simply choose the desired email content from the dropdown menu when creating an email element in the customer journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-emails-via-customer-journey/) ![Customer journey email element selection showing email content dropdown](https://docs.pushwoosh.com/content-email-content-4.webp) --- # Use AI tools in the Drag & drop editor **URL:** https://docs.pushwoosh.com/product/content/email-content/ai-tools-in-email-editor/ Find every AI feature in the Pushwoosh Drag & drop editor, from chat and images to News intros, subject lines, and translation --- Pushwoosh’s Drag & drop editor uses AI throughout the email-building workflow, from a chat assistant that edits your content to one-click translation and brand-matched styling. This page maps each AI tool to the guide that covers it in depth. ## AI tab Open the **AI** tab in the right panel to chat with Pushwoosh’s AI assistant: describe what you want in a prompt, and it generates or edits content for you directly in the email. ![AI tab in the email editor with a chat prompt and the generated re-engagement email on the canvas](https://docs.pushwoosh.com/emails-ai-tools-in-email-editor-1.webp) [See the editor layout ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#explore-the-editor-layout/) ## Generate images and alt text In the Image block, use **Generate with AI** to create an image from a text prompt, or **Generate alt with AI** to write accessible alt text from the image itself. [Learn more about the Image block ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image/) ## Write article intros for the News block In the News block, use **AI intros** to write a short editorial intro under each article’s headline, in a tone you pick. [Learn more about the News block ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#news/) ## Write subject lines and preheaders Click the **AI** icon next to the **Subject line** or **Preheader** field to have Pushwoosh draft it from the email content you’ve already added. [Learn more about subject lines ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#add-a-subject-line/) ## Translate content automatically Click **Auto-translate** in the language bar to fill in missing translations with AI instead of typing each language by hand. [Learn more about auto-translate ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#auto-translate-content/) ## Apply your brand automatically **Derive brand** in Brand book generates a starting palette, fonts, and Brand voice — scan your website URL, or derive them from a short description, an attached file, or your logo. [Learn more about Brand book setup ](https://docs.pushwoosh.com/product/content/brand-book/#set-up-your-brand/) --- # Get started with the Drag & drop editor **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/ Create professional emails without coding in the Pushwoosh Drag & drop editor. Set up languages, add blocks, personalize content, and style your campaigns. --- Use this guide to go from an empty canvas to a saved email: set up your email, build the layout, style the design, then review and save. Follow the steps below, or jump to a topic when you need it. ## Get started with the Drag & drop editor 1. **Set up your email** Choose your sending languages, and write the subject line and preheader. Recipients see these details before they even open the email. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/) 2. **Build the layout** Drag content blocks onto the canvas — images, text, buttons, timers, and more. Personalize copy with Merge Tags, or use [display conditions](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) to show different blocks to different recipients. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) 3. **Style the email and set the footer** Set fonts, colors, and spacing that apply across the whole email. Add your company name, postal address, and unsubscribe link to keep the email CAN-SPAM compliant. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/) 4. **Review and save** Switch to mobile view to check how the email looks on smaller screens. Run **Check** to catch missing fields or broken conditions, then save. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending). To roll back an earlier save, see [Restore a previous version](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#restore-a-previous-version). ## Explore the available blocks Headings, text, images, buttons, timers, and more — see every block you can drag onto the canvas and its settings. [Email content blocks reference ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/) ## Style your email Set document, typography, and color defaults for the whole email, and configure the compliant footer. [Configure email-wide styling and footer ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/) ## Show content to specific recipients Not every subscriber should see the same blocks. **Display conditions** show or hide a block based on a recipient’s [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/), so you send one email but each person sees only what applies to them. Use a single condition to hide an offer from non-members, or chain blocks with **Branch** so inactive buyers get a win-back message and everyone else gets a regular promo. [Show content to specific recipients ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) ## Apply consistent branding Save your logo, colors, fonts, and block defaults once as a **Brand book**, then apply them to any email in one click instead of styling each one from scratch. [Create a Brand book ](https://docs.pushwoosh.com/product/content/brand-book/) ## Use your saved email content After you save, your email appears in **Content** → **Email content**. Use it in a one-time campaign or a Customer Journey. [Learn more about sending emails ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/) --- # Email content blocks reference **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/ Reference for every content block in the Pushwoosh Drag & drop editor: heading, text, image, video, card, button, HTML, menu, social, timer, columns, news, ad slot, and links. --- Reference for every block you can add on the [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) canvas, and for the link types available on buttons and text. To control which recipients see a block, set a [display condition](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/). ## Add content blocks Open the **Blocks** tab and drag a block onto the canvas, or click a block to add it at the end of the email. Available blocks: * **[Heading](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#heading-and-text):** titles and short headlines in the email * **[Text](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#heading-and-text):** body copy and longer descriptions * **[Button](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#button):** a clickable call to action * **[Image](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image):** pictures from [Media storage](https://docs.pushwoosh.com/product/content/media-store/), upload, or URL * **[Logo header](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#logo-header):** your Brand book logo, as a ready-made header block * **[Video](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#video):** a clickable video poster that links to a YouTube or Vimeo video * **[Card](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#card):** an image paired with a heading, text, and button in one layout * **[Divider](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#divider):** a line that separates sections visually * **[2 columns](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** two equal columns side by side * **[3 columns](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** three equal columns side by side * **[4 columns](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** four equal columns side by side * **[33 / 67](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** two columns where the left takes about 33% of the width and the right about 67% * **[67 / 33](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** two columns where the left takes about 67% of the width and the right about 33% * **[Timer](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#countdown-timer):** a countdown for offers and deadlines * **[Barcode](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#barcode):** a scannable Code 128, EAN-13, EAN-8, UPC-A, or QR code generated from a personalization value, such as a voucher * **[Menu](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#menu):** a row or column of links, such as navigation or contacts * **[Social](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#social):** icons that link to your social profiles * **[HTML](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#html):** custom HTML, such as a form, survey, or embedded video * **[Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products):** a repeating grid of product cards. Enter products by hand, or pull them from your [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/), a recipient’s abandoned cart, or a built-in recommendation strategy such as bestsellers or back-in-stock. * **[News](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#news):** a repeating grid of article cards. Pick them by hand from an RSS or Atom feed, or pull them live from a feed registered for your application. * **[Ad slot](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#ad-slot):** a paid placement that shows a sponsored campaign to recipients ### Heading and text **Heading** and **Text** blocks hold the copy recipients read in the email. Use **Heading** for titles and short lines, **Text** for body copy and longer descriptions. Click a block on the canvas to start editing. Select text to bring up the formatting toolbar. ![Heading block selected on the canvas with the text formatting toolbar showing style and formatting options](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-9.webp) **Toolbar options** * **Text style:** set a paragraph style (Heading 1, Heading 2, Normal, etc.) for the whole block * **Font size:** resize selected text, or apply a size to the whole paragraph * **Bold / Italic / Underline / Strikethrough:** standard character formatting * **Alignment:** left, center, or right * **Link:** wrap selected text in a hyperlink * **Bulleted list / Numbered list:** turn the selected lines into a list. Any bullet or number the lines already start with is dropped, so it isn’t doubled with the list’s own marker. Select the block (not a text range) to open its settings in the right panel, then turn on **Letter spacing** to loosen or tighten character spacing for the whole block. Accepts fractional and negative values. To apply letter spacing across all text blocks at once instead, set it as an email-wide default under **Typography**. See [Configure email-wide styling](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/#configure-email-wide-styling). **Pasting from other documents** Pasting text from Google Docs, Word, or a web page keeps bold, italic, underline, strikethrough, and links. Color, font size, and font family from the source are not carried over — the block’s own typography always wins. To ignore the source formatting entirely and take on the block’s style, paste with **Ctrl+Shift+V** (**Cmd+Shift+V** on Mac). A pasted bulleted or numbered list keeps its bullet or number as plain text at the start of each line, in the same block. Select those lines and click **Bulleted list** / **Numbered list** to turn them into a real list — the button removes the leftover marker for you. In single-line fields, such as a **Button** label, pasted line breaks collapse into a single space. **Personalization with Merge Tags** Merge Tags are placeholders that insert a [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) value from the recipient’s profile, so each subscriber sees personalized copy. To add one, select the text, click **Merge Tags** in the toolbar, and choose a Tag from your account. Click an inserted Merge Tag to open a panel with **Default** (the value shown when the tag is empty) and **Format** (capitalization, number, or date formatting to apply to the value) fields. ### Image An **Image** block displays a picture in the email. Select it on the canvas to open the **Image** settings in the right panel. **Adding an image** Click **Add image** when the block is empty, or **Replace image** when a picture is already set. This opens the **Media Gallery** with three tabs: * **Library:** your uploaded files, searchable by file name. * **Stock photos:** search free Pexels photos without leaving the editor. Click one to copy it into your library and insert it. * **Generate with AI:** describe the image you want and pick an aspect ratio. The generated image is saved to your library. ![Media Gallery with Library, Stock photos, and Generate with AI tabs, and a search by file name field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-42.webp) You can also paste a hosted link directly into **Image URL** to skip the gallery. **Deleting images from the Library** Delete images without leaving the editor: hover a file and click **Delete**, or select several and click **Delete selected**, then confirm in the dialog. Caution Deleting a file removes it from the [Media storage](https://docs.pushwoosh.com/product/content/media-store/#delete-images) entirely, along with any crops created from it, and this can’t be undone. Any content that references the deleted image will stop loading it. **Alt text** Describe the image in **Alt text** for accessibility, or click **Generate alt with AI** to have AI write it from the image in your current editing language. Images added from Stock photos or Generate with AI get alt text automatically. **Size & fit** * **Width:** set the image’s width in px, or as a percentage of the available space so it stays fluid. Leave it at the default to use the image’s natural width. * **Height (px):** set a fixed render height. Leave empty to scale automatically — from the crop ratio if one is set, otherwise from the image’s natural proportions. * **Crop:** pick **Original** to keep the image’s natural proportions, or a ratio (**Square (1:1)**, **4:3**, **3:2**, **16:9**) to crop the image to that box. * **Fit:** choose **Cover** to fill the box and crop to it. Set the crop point with **Focal point**. Or choose **Contain** to scale the whole image inside the box without cropping. Position it with **Set position**. * **Corner radius (px):** round the image’s corners. Turn on **More options** to set each corner separately, or leave it off to apply the same radius to all four. Note Gmail and Apple Mail render rounded corners in the sent email, including per-corner values. Outlook Desktop ignores corner radius on images, the same as it does everywhere else in the email, so images stay square-cornered there. Switch the canvas to **Mobile** (see [Edit the mobile view](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#edit-the-mobile-view)) to set any of these separately for phone screens. Check **Width (mobile)**, **Height (mobile)**, **Crop (mobile)**, or **Fit (mobile)** to override that one setting, starting from its desktop value; leave a box unchecked to keep inheriting the desktop setting. **Crop (mobile)** only appears when **Crop** is set to a ratio on desktop, and changing **Crop** back to **Original** on desktop discards any **Crop (mobile)** override. ![Size and fit settings for an Image block in the mobile view, showing Width, Height, Crop, and Fit mobile-override checkboxes all set to inherit the desktop value](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-58.webp) **Links and spacing** To open a link when the image is clicked, go to **Link**, choose an **Action Type**, and enter the **URL**. See [Add links](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#add-links) for available link types. In **Spacing**, turn on **Padding** to add space around the image. Use **All Sides** for uniform padding, or turn on **More options** to set each side separately. ![Link and Spacing settings of an Image block with Action Type, URL, and Padding set to 10 px on all sides](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-10.webp) ### Logo header A **Logo header** takes its look from your [Brand book](https://docs.pushwoosh.com/product/content/brand-book/#logo) logo, so every email starts with the same header without rebuilding it each time. Click anywhere on the header to select the whole block. The logo inside it can’t be selected on its own. Select it on the canvas to open its settings in the right panel: ![Logo header settings panel with Logo, Dark mode image, Alignment, Width, URL, Background, Padding, and Bottom border](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-92.webp) * **Logo:** click **Choose logo** to pick the image from your [Media storage](https://docs.pushwoosh.com/product/content/media-store/), or paste a hosted image URL. * **Dark mode image:** set an image for dark mode. Apple Mail and Outlook show it when the recipient reads in dark mode. Gmail keeps the main image. * **Alignment:** left, center, or right. * **Width:** the logo’s width in px. * **URL:** enter a destination to make the logo clickable. * **Background:** turn on to set a background color behind the logo. * **Padding:** turn on to add space around the logo. Use **All sides** for uniform padding, or turn on **More options** to set each side separately. * **Bottom border:** turn on to draw a line under the header, then set its **Width** and color. ![Bottom border toggled on with Width and color fields](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-94.webp) How a header reacts to your Brand book depends on whether you’ve edited it: * **Not edited:** while a header keeps every setting the same as your Brand book, the panel reads “Follows the Brand book: Apply brand styles updates it.” [Apply brand book styles](https://docs.pushwoosh.com/product/content/brand-book/#apply-or-reset-brand-styles) repaints it from your saved brand along with the rest of the email. * **Edited:** change any setting on this header and the panel switches to “Settings changed here stay when the brand is applied.” That header keeps your edit from then on, even after **Apply brand book styles**. An edited header gets two more actions: ![Logo header panel in the edited state, showing the changed hint plus Save to Brand book and Reset to brand buttons](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-93.webp) * **Save to Brand book:** write this header’s look back to the Brand book. Every other Logo header in the email picks up the new look at once, while keeping any edits of its own. * **Reset to brand:** discard this header’s local edits and go back to following the Brand book. ### Video A **Video** block adds a YouTube or Vimeo video to the email as a clickable thumbnail. Email clients can’t play video inside the message, so the block shows a still image with a ▶ overlay. When the recipient taps it, the video opens on YouTube or Vimeo. This works in every major client, including Outlook. Select the Video block on the canvas to open its settings in the right panel. * **Video URL:** paste a YouTube or Vimeo link. The editor fetches a thumbnail automatically. * **Alt text:** describe the video for accessibility. * **Thumbnail URL:** keep the auto-fetched thumbnail, or click **Add thumbnail** / **Replace thumbnail** to pick one from the Media Gallery, or paste a hosted link directly. * **Play button overlay:** on by default. Turn off to show the plain thumbnail without the ▶ overlay. The overlay isn’t available for thumbnails on some external hosts. The editor warns you when that applies, and the email shows the plain thumbnail instead. * **Width (px)**, **Corner radius (px)**, and **Alignment:** control how the thumbnail looks and where it sits in the layout. You can set alignment separately for mobile. ![Video block settings with Video URL, Alt text, Thumbnail URL, Play button overlay, Width, Corner radius, and Alignment](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-44.webp) ### Card A **Card** block pairs an image with a heading, text, and button in a single ready-made layout. Select it on the canvas to open its settings in the right panel. ![Card block settings panel for the Left layout with Image, Size and fit, Spacing, and Block styles options](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-34.webp) * **Layout:** choose which side the image sits on relative to the content. * **Image:** replace the image, set alt text, or edit the prompt for an image you created with **Generate with AI**. * **Size & fit:** the size fields depend on **Layout**. **Crop** is available for all layouts. **Fit** applies to every layout: * **Top:** **Height** sets the image’s height in px or as a percentage of the card’s width. **Width (px)** narrows the image to a fixed pixel width and centers it. * **Background:** **Height** sets the card’s height in px or as a percentage of its width. There is no **Width (px)** field. The image is the card’s background. **Fit** works here too: **Cover** crops the image to the card — set the crop point with **Focal point** — and **Contain** scales the whole image inside it, placed with **Set position**. Outlook Desktop approximates **Contain** and ignores both. * **Left** or **Right:** **Width** sets the image column’s width in px or as a percentage of the card. **Height (px)** fixes the image to a pixel height. Setting a pixel height clears the crop ratio below. Picking a crop ratio clears the pixel height. You can’t use both at once. * For all layouts: * **Crop:** pick **Original** to keep the image’s natural proportions, or a ratio (**Square (1:1)**, **4:3**, **3:2**, **16:9**) to crop the image to that box. * **Fit:** choose **Cover** to fill the box and crop to it. Set the crop point with **Focal point**. Or choose **Contain** to scale the whole image inside the box without cropping. Position it with **Set position**. * **Corner radius (px):** round the image’s corners. Turn on **More options** to set each corner separately, or leave it off to apply the same radius to all four. Gmail and Apple Mail render it; Outlook Desktop ignores it, same as the [Image block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image). * **Spacing:** set the padding around the card’s content. ### Button A **Button** is a clickable call to action in the email. Click a **Button** block to edit its label on the canvas. Use the toolbar to change font size, text color, bold, italic, underline, strikethrough, and alignment. The label is a single line: pasted line breaks collapse into a space. See [Pasting from other documents](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#heading-and-text) for what a paste keeps and drops. To set what happens when the recipient clicks the button: * Click the link icon in the toolbar to open **Insert/Edit Link**. * Or use **Action type** and **URL** in the **Link** section of the right panel. ![Button block on the canvas with a formatting toolbar and Link settings in the right panel](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-11.webp) See [Add links](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#add-links) for the available action types. In the right panel, under **Styles**: * **Alignment:** left, center, or right. * **Background:** turn on to override the default look, then choose **Solid** for a solid color or **Gradient** for a two-stop linear gradient (a starting color, an ending color, and an angle in degrees, default 90°). With Gradient selected, the button still shows a solid fill in email clients that don’t support gradient backgrounds, such as Outlook on Windows. Buttons that already had a solid background keep it and open with **Solid** selected. * **Text color**, **Font size**, and **Font family:** turn each option on to override the default look. * **Corner radius**, **Border color**, and **Border width:** shape and outline of the button. Gmail and Apple Mail render the button’s corner radius; Outlook Desktop ignores it and shows square corners, the same as it does for images. * **Width:** set a fixed button width or leave it automatic. * **Padding:** add space inside the button. Use **All sides** for uniform padding, or turn on **More options** to set each side separately. ![Button Styles settings in the right panel with Alignment, Background, Text color, Font size, Corner radius, and Padding](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-12.webp) ### Divider A **Divider** is a horizontal line between content blocks. Use it to separate sections visually in the email. Configure **line style** (Solid, Dashed, Dotted), **color**, **thickness**, and **width** in the settings panel. ![Divider block settings with line style, color, thickness, and width options in the right panel](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-13.webp) ### HTML The **HTML** block lets you paste custom HTML into your email. Use it for a custom form, survey, or embedded video. Above the code field, click **Merge tag** to search your account’s [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) and insert a Merge Tag at the cursor position — the same personalization feature described above — or click the emoji icon to insert an emoji the same way. Click an inserted Merge Tag to open the same **Default** / **Format** panel as in a Heading or Text block. #### Examples * Lead form This lead form collects an email address to send a promo code for the New Year Sale. ![HTML lead form in the email editor with an email field and a Get my code button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-14.webp) HTML code ```html

Get your New Year Sale code

Enter your email and we'll send you an exclusive promo code for up to 50% off.

``` * Survey This survey asks what category the recipient is shopping for and links each option to the relevant sale page. ![HTML survey in the email editor asking what the user is shopping for with four category options](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-15.webp) HTML code ```html

What are you shopping for?

Pick a category and go straight to your deals

👗 Clothing 📱 Electronics 💄 Beauty 🏠 Home & Living ``` * Video This example embeds a New Year Sale promo video in the email. HTML code ```html

New Year Sale — Up to 50% Off

``` Note You can also add an unsubscribe link directly in the HTML block: ```html Unsubscribe ``` The **%%PW_EMAIL_UNSUBSCRIBE%%** placeholder is replaced with the Pushwoosh unsubscribe URL when the email is sent. ### Menu The **Menu** block places several clickable items in a row or column. Use it for a navigation bar or a list of company contacts. For each menu item, set: * **Label:** the text recipients see * **URL:** where the item opens * **Target:** same tab or a new tab In the block settings, you can also set: * **Direction:** Horizontal or Vertical * **Alignment:** Left, Center, or Right * **Separator:** an optional divider between items * Font, size, weight, letter spacing, and link/text colors ![Menu block settings with item labels, URLs, direction, alignment, and separator options](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-16.webp) ### Social The **Social** block displays a row of social media icons that link to your profiles. Use it to send recipients to Facebook, Instagram, X, and other platforms from the email. Add platform entries, then set alignment, icon size, and the gap between icons. ![Social block settings with platform links plus Alignment, Icon size, and Gap](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-17.webp) ### Countdown timer The **Timer** block is a countdown in the email. Use it for limited-time offers, flash sales, and event reminders. To set it up: 1. In **Countdown**, set **End date** and **End time**, then choose the **Timezone** and **Language** for the timer labels. 2. Turn **Labels** on to show “days / hours / minutes / seconds” captions under the digits, or off for numbers only. 3. In **Appearance**, set **Background**, **Digits color**, **Labels color**, and the font size for digits (default 40px) and labels (default 16px). 4. Adjust **Spacing** (padding) as needed. ![Timer block settings with Countdown end date and time, Labels, Appearance, and Spacing options](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-18.webp) The timer renders as a sharp image at any language or size, with a background that stretches across the full email row, and a version rendered natively for mobile width rather than a shrunk desktop image. ### Barcode The **Barcode** block renders a scannable barcode or QR code from a personalization value, most often a [voucher](https://docs.pushwoosh.com/product/content/vouchers/#display-a-voucher-as-a-barcode). To set it up: 1. In **Code**, keep `{{voucher}}` when the email is sent with a [voucher pool](https://docs.pushwoosh.com/product/content/vouchers/) or click **Merge tag** and choose the tag that stores each recipient’s code. Email resolves the tag at send time so each person gets their own scannable value. ![Barcode Code field with {{voucher}} and Merge tag button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-79.webp) 2. Choose **Symbology**: * **QR:** a square 2D code phones can scan. Accepts any text, including alphanumeric codes like `WELCOME2025-A1` * **Code 128:** a linear barcode for any text, including the same alphanumeric codes * **EAN-13:** the common retail barcode in Europe and many other regions. Digits only, 12–13 characters * **EAN-8:** a shorter retail barcode for small packaging. Digits only, 7–8 characters * **UPC-A:** the common retail barcode in North America. Digits only, 11–12 characters 3. If you chose **QR**, set **Error correction**. This adds spare data so the code can still scan when part of it is covered, worn, or hard to see. Choose **L**, **M**, **Q**, or **H** (default **M**). Higher levels survive more damage but make the pattern denser, which can be harder to read at small sizes. Skip this field for other symbologies. 4. Optionally turn on **Print the code in digits** to show the value under the code. ![Barcode Symbology set to QR, Error correction M, and Print the code in digits checked](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-75.webp) 5. In **Appearance**, set **Bars color**, **Background**, **Width (px)**, and **Height (px)**. Leave **Quiet zone, modules** on **Auto** for the default margin. ![Barcode Appearance settings with Bars color, Background, Width, Height, and Quiet zone](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-76.webp) Note On the canvas the block shows a sample barcode, not the real recipient code. `{{voucher}}` and other placeholders can’t be turned into a scannable image until send time. [Send a test email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#send-a-test-email) to see the actual code before the campaign goes out. Use **EAN-13**, **EAN-8**, or **UPC-A** only for a value that’s digits only, at the length each expects. For alphanumeric codes like `WELCOME2025-A1`, use **Code 128** or **QR** instead. [Smart check](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending) also warns you if the value doesn’t match the **Symbology** you chose. ### Columns **Columns** split the canvas into side-by-side sections so you can place content next to each other in one row. Drag one of these blocks onto the canvas: * **2 columns:** two equal columns * **3 columns:** three equal columns * **4 columns:** four equal columns * **33 / 67:** left column about 33% wide, right about 67% * **67 / 33:** left column about 67% wide, right about 33% Then add content to each column. To change spacing for all columns in the email, open **Settings** → **Columns defaults** and adjust outer padding, column padding, and the gap between columns. For the column’s own image (the one you add with **Add image** in the column’s settings), the settings show a **Size & fit** section. This is the same control as the Card block’s **Left** / **Right** layout. An **Image** block dragged into a column is a separate content element and keeps its own simple sizing fields — see [Image](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image). You can also paste a hosted link directly into **Image URL** to skip the gallery. * **Width:** set the image’s width in px or as a percentage of the column. Leave it unset to fill the column. * **Height (px):** fix the image to a pixel height. Setting a pixel height clears the crop ratio below. Picking a crop ratio clears the pixel height. You can’t use both at once. * **Crop:** pick **Original** to keep the image’s natural proportions, or a ratio (**Square (1:1)**, **4:3**, **3:2**, **16:9**) to crop the image to that box. * **Fit:** choose **Cover** to fill the box and crop to it. Set the crop point with **Focal point**. Or choose **Contain** to scale the whole image inside the box without cropping. Position it with **Set position**. * **Corner radius (px):** round the image’s corners. Turn on **More options** to set each corner separately, or leave it off to apply the same radius to all four. Gmail and Apple Mail render it; Outlook Desktop ignores it, same as the [Image block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image). Note A column image with only **Height (px)** set stretches to the column’s width and crops to that height. ### Products A **Products** block shows a grid of product cards from one shared template. Each card can include an image, title, description, price, and a buy button. The template repeats for every product. ![Products block on the canvas with a grid of product cards](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-51.webp) Click a card on the canvas to make it active and edit it: * Change the title, description, price, picture, or per-product link to update only that card. * Change the template layout, colors, padding, or which elements it contains to update every card. In the settings panel, under **Source**, choose where the products come from: * **Manual:** enter products by hand, pick them from your Product catalog by ID, or mix both. * **Catalog rule:** pull products automatically by category and sort order, resolved fresh from the catalog at send time. * **Cart:** show the items from each recipient’s current abandoned cart. * **Recommendations:** pull products ranked by a built-in strategy — bestsellers, new arrivals, back in stock, or price drop — resolved from your catalog at send time. Note These sources need **Content Templates** on your plan. If the **Source** selector won’t switch to **Catalog rule**, **Cart**, or **Recommendations**, contact Support or your account manager. You also need an [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) (**Settings → API Access**) to save the email. Previewing, running [Smart check](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending), and everything else in the editor work without one. Only **Save** fails, with a toast telling you the block has no API access token. #### Add products manually Fill the grid yourself, either by entering products by hand or by picking specific items from your [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/) by ID. You can mix both in the same block. * **Number of products:** set how many cards to show (1–24). * **Add product:** add a card and fill in its title, description, price, picture, and link yourself. * **Pick from catalog…:** select specific products from your [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/) instead of typing them in. * **Refresh from catalog:** pull the latest title, description, price, and image for products you picked from the catalog, in case they changed since you added them. Does not affect hand-entered products. * **Product images:** click a thumbnail to replace that product’s picture. * **Product links:** enter or edit the URL for each product. ![Manual source settings for the Products block with Add product, Pick from catalog, Refresh from catalog, Product images, and Product links](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-55.webp) #### Pull products with a catalog rule Automatically pull products by category and sort order, with no manual picking. * **Category:** limit to a specific catalog category, or **All categories**. * **Sort:** choose the order products are pulled in: **Newest first**, **Price: low to high**, **Price: high to low**, or **Title A-Z**. * **Number of products:** set a limit on how many cards to show (1–12). * **Refresh preview:** recalculate the canvas preview against the catalog right now. ![Catalog rule source settings for the Products block with Category, Sort, Number of products, and Refresh preview](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-56.webp) Preview vs send time The canvas preview shows the products that match the rule right now. If the catalog changes before you send the email, for example a product goes out of stock or a new one is added, recipients see the updated match at send time, not the preview. #### Show items from the cart Render the current items from a recipient’s abandoned cart. * **Max products shown:** set a limit on how many cards to show (1–12). * **Preview a user’s cart:** enter a user ID or email and click **Preview cart** to see how the block renders for that specific recipient. ![Cart source settings for the Products block with Max products shown and Preview a user's cart](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-57.webp) Note Each recipient gets their own current abandoned cart at send time. Recipients without one simply skip the block, so target your audience using the `PW_AbandonedCartID` tag. The canvas itself shows sample cards, not real cart data. See [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/) for the full setup. #### Get recommended products Pull products ranked by a built-in strategy instead of picking them yourself or writing a catalog rule. ![Products block settings with Source set to Recommendations, a Strategy selector set to Bestsellers (30 days), Category, Number of products, and Refresh preview](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-69.webp) * **Strategy:** choose how products are ranked — **Bestsellers (7 days)**, **Bestsellers (30 days)**, **New arrivals**, **Back in stock**, **Price drop**, **Bought together**, or **Based on what they viewed**. Selecting **Recommendations** replaces the **Sort** control from **Catalog rule** with this **Strategy** selector. Bestsellers, Bought together, and Based on what they viewed need events from your backend. See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/). ![Strategy selector expanded showing Bestsellers (7 days), Bestsellers (30 days), New arrivals, Back in stock, Price drop, Bought together, and Based on what they viewed](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-70.webp) * **Category:** limit to a specific catalog category, or **All categories**. * **Number of products:** set a limit on how many cards to show (1–12). * **Refresh preview:** recalculate the canvas preview against the catalog right now. Bought together needs a Product tag Selecting **Bought together** adds a **Product tag** field. Enter the name of the device tag that holds the anchor product, and the block shows products that the same shoppers interacted with alongside it. Without a tag set, the block falls back to the regular catalog. ![Product tag field for the Bought together strategy with placeholder PW_LastViewedProductID and a hint explaining the tag holds a product ID the recommendations are built from](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-71.webp) The tag name only accepts letters, digits, underscores, and spaces — anything else is rejected with an inline error, since the tag has to be addressable in Liquid. ![Product tag field showing an invalid tag name with the inline error: this tag name is not addressable in Liquid, only letters, digits, underscores and spaces work](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-72.webp) Note **Bestsellers** ranks products by order history specifically. **Bought together** builds from the same broad interaction stream as **Based on what they viewed**: views, cart, and purchases alike, not purchases exclusively. It counts activity within one application over the last 30 days. New accounts, or accounts that haven’t collected enough history yet for a given strategy, get the regular catalog instead until enough accumulates. Products block settings aren't translated yet The Products block’s settings, including the **Recommendations** source name and its strategy labels, show in English regardless of your account’s language. #### Style the grid Once you’ve picked a source, these settings control how the grid and its cards look. They stay the same no matter which source you use: * **Columns:** choose 1, 2, or 3 cards per row. On mobile, cards always stack in one column. * **Gap:** turn on to set the space between cards. Off uses the default (16px). * **Description limit:** the character limit the product description is trimmed to, so cards stay a consistent size. * **Typing a value:** type freely. The field only normalizes on blur or Enter, flooring at 10. Unlike the [News block’s Summary limit](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#style-the-grid-1), 0 doesn’t hide the description here. Turn the toggle off instead for no cap. * **What counts toward the cap:** only the text you’d actually read. Bold, links, and line breaks are free. Typing past the limit in the description field is blocked. * **Card template:** pick a layout, then set **Card background**, **Card corner radius**, and **Content padding** for every card. * **Size & fit:** shown for every layout that has an image. Same **Width**, **Height**, **Crop**, and **Fit** controls as the [Card block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#card)’s per-layout image sizing, sized relative to the card width instead of the whole email. **Block style** settings below style the block as a whole, separately from the cards inside it: * **Background** and **Background image:** set a background color or image behind the whole grid. With an image set, **Fit** chooses how it sits in the block — **Cover** crops it to the block, **Contain** fits the whole image inside it — and **Focal point** / **Set position** places it. Outlook Desktop shows the background color only and ignores the image, so keep a readable color behind it. * **Border color**, **Border width**, and **Corner radius:** add an outline and rounded corners around the whole grid. Turn on **More options** in Corner radius to set each corner separately, or leave it off to apply the same radius to all four. Gmail and Apple Mail render it; Outlook Desktop ignores it and shows square corners, the same as it does for images. * **Border sides:** once **Border width** is set, choose **All**, **Top**, **Bottom**, or **Top & bottom** to set which sides of the grid get the border. * **Hide on mobile** and **Hide on desktop:** hide the entire block on one device. See [Show or hide content per device](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#show-or-hide-content-per-device). ![Border width turned on with the Border sides control set to Top & bottom](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-96.webp) How sale price appears If a product has a sale price in your catalog, the card shows the discounted price with the original struck through. This applies to **Catalog rule**, **Cart**, and catalog items in **Manual**. Hand-entered products have no sale price. ### News A **News** block shows a grid of article cards. Each card can include an image, date, headline, summary, and a “Read more” link. The template repeats for every article, the same way it does for the [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block. A new News block starts with 2 sample cards. ![News block card on the canvas with an article image, date, headline, summary, and Read more button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-60.webp) In the settings panel, under **Source**, choose where the articles come from: * **Picked:** load articles from an RSS or Atom feed once, or hand-pick specific ones. They’re copied into the email and don’t change again. * **Live feed:** pull articles from a feed registered for your application, resolved fresh at send time so every recipient gets the newest ones. Note **Live feed** needs **Content Templates** on your plan. If **Source** won’t switch to **Live feed**, Pushwoosh shows a hint that dynamic content isn’t enabled for the account. Contact Support or your account manager. #### Pick articles from a feed Click a card on the canvas to make it active and edit it: * Change the date, headline, summary, image, or per-article link to update only that card. * Change the template layout, colors, padding, or which elements it contains to update every card. Articles are copied into the email content once you load them; nothing is fetched again at send time. Article images are copied into your [Media storage](https://docs.pushwoosh.com/product/content/media-store/) and converted from WebP to JPEG if needed, since Outlook can’t render WebP. Paste an RSS 2.0 or Atom **Feed URL**, then either: * **Load latest articles:** replace the current cards with the most recent articles from the feed, the same number of cards you already have (2 by default). * **Pick articles…:** open a drawer listing up to 12 of the feed’s latest articles, with thumbnail, headline, date, category, and author, and choose which ones to add. Picked articles are appended to the existing cards, up to 12 total; articles already on the canvas can’t be picked twice. Note If the feed URL is empty, invalid, or the feed has no articles, Pushwoosh shows an error and leaves the current cards unchanged. ![News settings panel with Source set to Picked, a Feed URL field, and Load latest articles and Pick articles buttons](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-61.webp) The drawer lets you hand-pick specific articles instead of always taking the newest ones. ![Pick articles drawer listing feed articles with thumbnails, headlines, dates, and checkboxes to select](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-62.webp) * **Article images:** click a thumbnail to replace that article’s image. * **Article links:** shown when the card template has a link bound to the article (the default “Read more” button). Lists each article by headline so you can edit or override its destination URL, initially pulled from the feed. #### Write article intros with AI Add a short editorial intro under each article’s headline, written by AI in the tone you choose. AI intros work only with **Source** set to **Picked**, not **Live feed**. 1. In the News block settings, go to the **AI intros** section. 2. Set **Tone**: **Neutral**, **Why it matters**, or **Witty**. 3. Optionally, fill in **Newsroom guidance** with your voice, words to avoid, or the audience. Up to 500 characters. 4. Click **Write intros with AI**. ![AI intros settings section in the News block with Tone, Newsroom guidance, and the Write intros with AI button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-100.webp) Pushwoosh writes an intro for every card in the block that doesn’t have one yet. It writes from the article’s own page and uses the feed summary only when the page can’t be read. The AI is told to write at most 280 characters, and Pushwoosh trims any excess so the cap always holds. The intro appears as its own field under the headline, above the summary. If the card template has no place for it, Pushwoosh adds one there. If the template has no headline field either, the intro becomes the card’s first field instead. To change an intro, click it on the canvas and edit it the same way as the headline or summary. The 280-character cap applies only to what the AI writes, not to a manual edit. ![News card on the canvas showing a generated AI intro below the headline and above the original summary](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-101.webp) To replace all intros in the block, click **Rewrite all intros**. This button appears instead of **Write intros with AI** once every card has an intro. If some intros aren’t written, Pushwoosh shows one of these messages: * **“Intros not written for N articles — try again”:** some or all articles didn’t get an intro. N counts every article that failed. The intros that were written stay. Click **Write intros with AI** again. It writes only the cards that still don’t have one, not a full rewrite. * **“Could not write the intros”:** the request itself failed, for example because of a network or server error. Tip To avoid showing the same lede twice, set [Summary limit](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#style-the-grid-1) to 0 in **Style the grid**. Cards then show the intro without the summary text. #### Pull articles from a live feed Articles are fetched when the email is sent, so every recipient gets the freshest ones. Instead of picking articles yourself, choose a feed and a limit: ![News settings panel with Source set to Live feed, a Registered feed selector, Category filter, Articles count, and Refresh preview button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-73.webp) * **Registered feed:** choose a feed already registered for your application, or **All feeds of the application** to pull from all of them. This panel only picks a feed, it doesn’t register one; if none exist yet, add one on the [**News feeds**](https://docs.pushwoosh.com/product/content/news-feeds/) page using **Add feed**. * **Category filter:** an optional free-text filter matched against the article’s category from the feed (default: any category). * **Articles:** how many cards to show (1–12, 3 by default). * **Refresh preview:** recalculate the canvas preview against the feed right now. If the feed returns no articles, Pushwoosh leaves the current cards unchanged; if the feed can’t be reached, Pushwoosh shows an error instead. Preview vs send time The canvas preview shows articles that match right now. Recipients get the match resolved fresh at send time, deduplicated by each article’s feed GUID and limited to articles published in the last 30 days. Note A missing, empty, or unreachable feed, including a registered feed that was deleted, doesn’t fail the send: Pushwoosh hides the card grid for that recipient, while the block’s background and border still render. Only a misconfigured feed reference, for example a corrupted application or feed source, falls back instead to the cards already on the canvas. #### Style the grid Once you’ve picked a source, these settings control how the grid and its cards look. They stay the same no matter which source you use: * **Columns:** choose 1, 2, or 3 cards per row. On mobile, cards always stack in one column. * **Gap:** turn on to set the space between cards. Off uses the default (16px). * **Summary limit:** turn on to cap the article summary at a set number of characters (default 160), so cards stay a consistent size. * **Typing a value:** type freely. The field only normalizes on blur or Enter, rounding 1–9 up to 10. * **Off or zero:** off means no cap, so the whole summary renders. Set it to 0 to drop the summary entirely from the card, the sent email, and a live feed cell. The panel then shows “Summary is hidden: the cards show no article text (use it with AI intros).” * **What counts toward the cap:** only the text you’d actually read. Bold, links, and line breaks are free. Typing past the limit in the summary field is blocked. * **Card template:** pick a layout, then set **Card background**, **Card corner radius**, and **Content padding** for every card. For the **Left** or **Right** layout, turn on **Wrap text around image** to flow the headline and summary around the picture instead of keeping them in a fixed column. **Size & fit** (Width, Height, Crop, Fit) works the same as the [Card block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#card)’s per-layout image sizing, sized relative to the card width. **Block style** settings below style the block as a whole, separately from the cards inside it: * **Background** and **Background image:** set a background color or image behind the whole grid. With an image set, **Fit** chooses how it sits in the block — **Cover** crops it to the block, **Contain** fits the whole image inside it — and **Focal point** / **Set position** places it. Outlook Desktop shows the background color only and ignores the image, so keep a readable color behind it. * **Border color**, **Border width**, and **Corner radius:** add an outline and rounded corners around the whole grid. Turn on **More options** in Corner radius to set each corner separately, or leave it off to apply the same radius to all four. Gmail and Apple Mail render it; Outlook Desktop ignores it and shows square corners, the same as it does for images. * **Border sides:** once **Border width** is set, choose **All**, **Top**, **Bottom**, or **Top & bottom** to set which sides of the grid get the border. * **Hide on mobile** and **Hide on desktop:** hide the entire block on one device. See [Show or hide content per device](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#show-or-hide-content-per-device). Note Like any other block, a News block supports [display conditions](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) to show it only to a subset of recipients. ### Ad slot An **Ad slot** block shows a sponsored campaign to email recipients. It’s a paid placement your account manager sells against, not a block you fill with your own creative. Note **Ad slot** needs to be enabled for your account. If it isn’t in your **Blocks** tab, contact Support or your account manager. Once it’s on, create and manage the campaigns it can show from the [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) screen. Select the block on the canvas to open its settings in the right panel. ![Ad slot settings panel with Campaign ID, Category, Label above the creative, and a When nothing is sold toggle set to Hide the slot](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-80.webp) * **Campaign ID:** the numeric ID of the ad campaign to show. Leave it at `0` to match any Active campaign instead. The [ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) list shows no ID for a campaign — copy the number from the browser address bar on the campaign’s own page after you save it. * **Category:** limit the match to campaigns tagged with this category. Set it together with **Campaign ID** to require both — the ad only shows if that exact campaign also carries this category. Leave **Campaign ID** at `0` to match any Active campaign in the category instead. * **Label above the creative:** a short line shown above the ad, for example “Sponsored”. Leave it empty to show no label. * **When nothing is sold:** choose what the block shows if no campaign matches. **Hide the slot** (the default) leaves nothing in its place. **Show my own copy** adds a second section inside the same block, marked “Shown when no campaign is running,” where you write your own stand-in text and add an image. That section ships in the email instead of an ad whenever no campaign matches. Leaving it empty saves as **Hide the slot** — an empty stand-in never ships as a blank block. ![Ad slot block on canvas with When nothing is sold set to Show my own copy, showing the ad placeholder above a dashed divider and the stand-in copy Your own promo here below it](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-81.webp) The canvas shows the ID, category, or label you set as a placeholder — not the actual ad creative. Pushwoosh picks the matching campaign once, when the email starts sending, so every recipient of that send sees the same ad — there’s no per-recipient targeting for which campaign shows, and no frequency capping in this release. Note Like any other block, an Ad slot block supports [display conditions](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) to show it only to a subset of recipients. That’s separate from campaign matching above. A display condition controls who sees the block at all, not which campaign it shows. Note Whether **Ad slot** renders depends on the account that owns the email, not on the template. Duplicating a template doesn’t change this — a copy made from an account where **Ad slot** isn’t enabled never shows the ad either. If **Ad slot** is enabled or disabled for your account after you’ve built the email, saving it again applies the change. Turn it off and save, and the ad stops showing. Turn it back on and save, and it resumes. ## Save and reuse blocks Save a block to reuse it in this or other emails instead of rebuilding it each time. 1. Click a block (its frame or background, not the text or button inside it) to select it. Card, Columns, and Social are examples of blocks that work this way. Tip To save several blocks together, Shift-click more sibling blocks, on the canvas or in the **Layers panel**, to extend the selection to a contiguous range. Save isn’t offered for a range that includes an already-synced block or an empty AI placeholder. 2. Click the bookmark icon in the block’s toolbar and give it a name. ![Bookmark icon in a Card block's toolbar opening a dialog to name and save the block](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-38.webp) Saved blocks appear under **Saved blocks** at the bottom of the **Blocks** tab. ![Saved blocks section at the bottom of the Blocks tab with saved block tiles and delete icons](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-39.webp) * Drag a saved block onto the canvas, or click it to add it at the end of the email. * Click the **×** on a saved block’s tile to delete it. ### Keep copies in sync When you save a block, turn on **Keep copies in sync** to link every inserted copy to the same source. Edit the source once, and the change applies everywhere that block is used, including emails already scheduled to send. Saving a range of blocks works the same way. Edit any block inside it, then apply the change to update every copy wherever it’s used. ![Save dialog for a 3-block range with Keep copies in sync turned on](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-50.webp) A synced copy is locked on the canvas. You can’t edit it directly. Select the copy to open its settings: * **Edit synced block:** unlock this copy so you can change it. A banner appears at the top. Click **Apply to all** to save your changes to the source and every other copy, or **Cancel** to discard them. You can unlock only one synced block at a time. * **Update from source:** replace this copy with the latest version from the source. * **Detach from source:** unlink this copy from the source so you can edit it on its own. While a synced block is unlocked for editing, the rest of the email is locked: you can’t select, edit, or delete anything else, and **Save** is disabled until you finish. Click **Apply to all** to save your changes everywhere, or **Cancel** to discard them and unlock the rest of the email again. Note Each time you open an email, Pushwoosh checks its synced copies and updates any that are out of date. A toast shows how many were refreshed. If the source has newer content, the copy’s settings show a warning until you reopen the email or click **Update from source**. If the source was deleted, the copy keeps its last content and you can only **Detach from source**. ## Add links Use the **Insert/Edit Link** dialog to set what happens when a recipient clicks a button or selected text. Choose an **Action type**, then fill in the fields for that action. ### Open Website Opens a link when the recipient clicks. Enter the address in **URL**. ![Insert Edit Link dialog with Action type Open Website and a URL field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-20.webp) ### Manage preferences link Adds a link to the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#preference-center-link) where recipients can choose which email categories they receive. The link is inserted automatically. ![Insert Edit Link dialog with Manage preferences link selected as the action type](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-21.webp) ### Unsubscribe link Lets recipients opt out in line with regulations and their preferences. ![Insert Edit Link dialog with Unsubscribe link selected as the action type and an optional redirect field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-22.webp) Tip You can also add an unsubscribe link in an [HTML](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#html) block: ```html Unsubscribe ``` The **%%PW_EMAIL_UNSUBSCRIBE%%** placeholder is replaced with the Pushwoosh unsubscribe URL when the email is sent. #### What happens when the recipient clicks the unsubscribe link ##### If the subscription preference center is enabled The recipient is unsubscribed only from the category assigned to that email (e.g., *Newsletter*). The category name is removed from their [**Subscribed Email Categories**](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#categories-and-subscriptions) tag. Depending on your unsubscribe flow, the recipient may first confirm the unsubscribe and answer a short survey. The final page offers **Resubscribe** and **Manage preferences** options. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#what-happens-when-a-user-clicks-the-unsubscribe-link). ##### If the subscription preference center is not enabled The recipient is opted out of all marketing emails and the [**Unsubscribed Emails**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags) tag is set to `true`. Transactional emails are not affected and will continue to be delivered. [Learn more about message types](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). Important To let you deliver important emails to your customers, we do not delete unsubscribed users from your user base. To continue messaging engaged recipients, create a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) filtered by **Unsubscribed Emails = false**. To avoid complaints, do not email recipients who have unsubscribed. Clicks are counted in the unsubscribe rate in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history). ### Web version link Gives recipients access to a web version of the email, useful if they have trouble viewing it in their inbox. ![Insert Edit Link dialog with Web version link selected as the action type](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-23.webp) ### No track statistics Opens a link when the recipient clicks, without counting the click toward your email’s click statistics. Enter the address in **URL**. ![Insert Edit Link dialog with No track statistics selected as the action type and a URL field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-37.webp) ### Subscription form confirmation link Inserts a confirmation link for a subscription form submission (for example, to confirm a double opt-in). [Learn more about double opt-in](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/). ![Insert Edit Link dialog with Subscription form confirmation link selected as the action type](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-24.webp) --- # Configure email-wide styling and footer **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/ Set document, typography, and color defaults for the whole email, and configure the email footer in the Drag & drop editor. --- ## Configure email-wide styling In the Drag & drop editor, with your email open on the canvas, open the **Settings** tab in the right-hand panel. ![Settings tab open with Document, Color scheme, Typography, Sender details, and block defaults sections](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-82.webp) These settings apply to the whole email. To change one block only, select it on the canvas and override the style there. Note The dashed outlines around blocks on the canvas are editor-only guides. They aren’t part of the email recipients receive. The only dashed line that appears in the sent email is the **Divider** block’s dashed line style, if you choose it. Some defaults, such as visibility, can be set separately for desktop and mobile. See [Edit the mobile view](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#edit-the-mobile-view). ### Document Set the size and look of the whole email area: * Width and padding * Background image (with its own **Fit** and position) or gradient * Gap between blocks * Default image corner radius, content area corner radius, and content shadow Set the solid background color for the whole email under **Color scheme**, not here. ### Color scheme Set the email’s palette: * **Background:** solid color behind the entire email, including padding around cards and other blocks * **Text:** page text * **Heading:** heading styles * **Link:** menu links that don’t have an explicit color Other palette colors are used when you pick colors later in the editor and when you use AI to generate content. Blocks already on the canvas keep the colors you set on them. Turn on **Dark mode variant** to set separate colors for clients that honor dark mode. ### Typography Set the email-wide font family. For each text style (Heading 1, Heading 2, Normal, and so on), set font size, font weight, line height, and letter spacing. ### Sender details In **Sender details**, set who the email is from and where replies go: **From name** and **From email** for the sender recipients see, **Reply-to name** and **Reply-to email** for where their replies land. ![Settings Sender details empty: From name, From email, Reply-to name, Reply-to email with placeholders only](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-88.webp) ### Block and layout defaults Use these so new blocks match the email until you style a block on the canvas: * **Block defaults:** background, background image, border color, border width, corner radius, and mobile/desktop visibility * **Columns defaults:** outer padding, padding inside each column, and gap between columns * **Card defaults:** starting look for Card containers * **Button group defaults:** spacing and alignment for buttons in a group * **Button defaults:** starting look for new Button blocks * **Divider defaults:** line style, color, thickness, and width Defaults apply only where a block does not set its own value. If you change **Corner radius** on a block, that setting replaces the email-wide default for all corners on that block. It does not mix corners from the default and the block. ## Configure the email footer In the **Layers** panel, select **Footer** to open its settings in the right-hand panel. Every email includes a **Footer** block for legally required sender information and the unsubscribe link. ![Layers panel with Footer selected, blue highlight on the row, and a red arrow pointing to Footer](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-26.webp) Note Display conditions apply to blocks on the canvas, not the **Footer**. [Learn more about display conditions](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/). ### Load or save a project footer * **Load from project:** fill this email’s footer from your saved project-level footer. * **Save as project default:** store the current footer as the starting point for future emails. Use this only when you want new emails to start from this footer. Defaults are saved for this project only. ![Footer panel with Load from project and Save as project default, and note that defaults are project-only](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-83.webp) ### Email type and company info 1. Under **Email type**, choose **Marketing** or **Transactional**. For **Marketing**, the footer keeps the unsubscribe link. For **Transactional**, the unsubscribe link is omitted. Unsubscribe visibility follows this setting. [Learn more about marketing vs. transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). 2. Set the logo URL or pick a logo from the gallery. This logo appears in the footer, at the bottom of the email. To show your logo at the top of the email, add a [Logo header](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#logo-header) block, which takes its logo from your Brand book. 3. Set the logo width in px. 4. Optionally set a **Dark mode image** URL, or pick an image from the gallery. Apple Mail and Outlook show it in place of the main logo when the recipient reads in dark mode. Gmail always keeps the main logo. Applying a [Brand book](https://docs.pushwoosh.com/product/content/brand-book/#logo) that has a logo replaces both the footer logo and its dark mode image with the Brand book ones. 5. Optionally set a click-through link: choose an **Action Type** and enter the URL. 6. Fill in **Company name**, **Postal address**, and **Copyright**. ![Footer Email type Marketing, CAN-SPAM note, Logo URL set with width, Dark mode image, and click-through Action Type/URL fields shown, empty Company name, Postal address, Copyright](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-84.webp) Caution Marketing emails must include a physical postal address (CAN-SPAM). ### Links and badges 1. Add labeled **Footer links**, such as Privacy Policy or Terms. 2. Add **App store badges** for App Store or Google Play with destination URLs. 3. Add **Social links** with icons that point to your social profiles. ![Footer Links and badges only: Footer links, App store badges, and Social links with Add buttons](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-85.webp) ### Style * **Text color:** footer text color * **Font size (px):** footer font size * **Alignment:** left, center, or right * **Underline links:** turn on to underline footer links ![Footer Style only: Text color, Font size, Alignment Left Center Right, and Underline links](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-86.webp) ### Options Set **Unsubscribe link text** for the managed unsubscribe link on marketing emails. Transactional emails omit the link. ![Footer Options only: Unsubscribe link text field and note that transactional emails omit it](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-87.webp) ## Check before you save When the footer and email-wide settings look right on the canvas, run **Check** before you save. See [Check your email before sending](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending). --- # Create email content with Drag & drop editor **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/ Build email content without coding in the Pushwoosh built-in email editor. Add images, buttons, personalized text, and a compliant footer for your campaigns. --- Build email content in the Pushwoosh built-in editor without writing code. You get a reusable layout with content blocks, images, buttons, personalized text, and a compliant footer that you can use in a Customer Journey or a one-time email. Note Prefer HTML? Use the Pushwoosh HTML code editor for more control. [Learn more](https://docs.pushwoosh.com/product/content/email-content/html-code-editor) Important Before you start, set up your email’s languages, subject line, and preheader. [Learn how](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/) ## Create new email content 1. Go to **Content** → **Email content**. 2. Choose how to start: * Click **Create email content** to open the builder’s template gallery. Pick a template to start pre-filled and brand-recolored, or **Blank email** to start from scratch. [Learn more](https://docs.pushwoosh.com/product/content/email-content/email-templates-library/) * Click the arrow next to **Create email content**, then select **HTML code** to use the HTML code editor. [Learn more](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/) ![Create email content button with a menu showing the HTML code option](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-1.webp) ## Explore the editor layout Once you open an email, the built-in editor has these areas: **Top bar** Rename the email, undo or redo changes, run **Check** to catch issues before you send, and save your work. See [Check your email before sending](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending). Open the **⋮** menu for more actions: * **Preview:** see the email as it will render for recipients, with its own Desktop/Mobile toggle. See [Edit the mobile view](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#edit-the-mobile-view). * **Test email:** send a preview to a test device or address. See [Send a test email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#send-a-test-email). * **Brand book:** apply your saved logo, colors, fonts, and block defaults to this email in one click. [Learn more](https://docs.pushwoosh.com/product/content/brand-book/) * **Brand styles** section: apply your saved brand or reset per-block style overrides without opening Brand book. [Learn more](https://docs.pushwoosh.com/product/content/brand-book/#apply-or-reset-brand-styles) * **Version history:** view and restore an earlier saved version of this email. See [Restore a previous version](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#restore-a-previous-version). ![Top bar of the email editor with template name, Undo, Redo, Test email, and Save](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-2.webp) Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, the top bar also shows a **Submit for approval** button and a version status chip (**Draft**, **On approval**, **Approved**, or **Rejected**), and **Test email** is hidden entirely. See [Approvals and content versions](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) for the full workflow. ![Email content editor showing the Draft version chip next to a Submit for approval button](https://docs.pushwoosh.com/approvals-and-content-versions-7.webp) **Language / Subject / Preheader bar** Above the canvas, switch the editing language, set the subject line and preheader for the email, and use **Auto-translate** to fill in missing translations. [Learn how to set up languages and localization](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#define-the-languages-you-want-to-use-in-your-email). ![Language, subject, and preheader bar above the canvas with a language switcher and text fields](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-3.webp) **Canvas** See the email as recipients will see it, and edit text, images, and buttons directly on the layout. An empty email shows **Start with a block**. ![Empty canvas in the email editor with the Start with a block prompt](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-4.webp) **Layers panel** Lists every block in your email as a tree, in the same order as on the canvas. Open it from the left edge to select a nested block or drag rows to reorder content. ![Layers panel listing Card, Image, Heading, Text, and Footer in a tree](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-5.webp) **Right panel** Switch between three tabs: * **Blocks:** add content to the email. See [Email content blocks reference](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/). * **Settings:** apply styling to the whole email at once. See [Configure email-wide styling and footer](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/). * **AI:** chat with the AI assistant to generate or edit content from a prompt. ## Add content blocks 1. Open the **Blocks** tab in the right panel. 2. Drag a block onto the canvas, or click a block to add it at the end of the email. 3. Keep adding blocks until the layout is ready. Each block type serves a different purpose in the email: * **[Heading](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#heading-and-text):** titles and short headlines * **[Text](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#heading-and-text):** body copy and longer descriptions * **[Button](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#button):** a clickable call to action * **[Image](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image):** pictures from [Media storage](https://docs.pushwoosh.com/product/content/media-store/), upload, or URL * **[Video](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#video):** a clickable video poster that opens on YouTube or Vimeo * **[Card](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#card):** an image with heading, text, and button in one layout * **[Divider](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#divider):** a line that separates sections * **[Columns](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns):** side-by-side layouts (2, 3, or 4 equal columns, or 33/67 and 67/33 splits) * **[Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products):** a grid of product cards — enter products by hand, or pull them from your [Product catalog](https://docs.pushwoosh.com/product/content/product-catalog/) or a recipient’s abandoned cart * **[Timer](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#countdown-timer):** a countdown for offers and deadlines * **[Menu](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#menu):** a row or column of navigation or contact links * **[Social](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#social):** icons that link to your social profiles * **[HTML](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#html):** custom HTML, such as a form, survey, or embedded video See the [Email content blocks reference](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/) for settings for each block, [how to save and reuse blocks](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#save-and-reuse-blocks), and [link types](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#add-links) for buttons and text. ### Show content to specific recipients To show a block only to some recipients, set a [display condition](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/). ## Edit the mobile view Once you’ve added your content blocks, switch to mobile to check and adjust how they look on a phone. Next to the language switcher above the canvas, use the **Desktop / Mobile** toggle to switch into a fully editable mobile view at real phone width. Select, edit, and rearrange content exactly the way your subscribers will see it on their phone, instead of just previewing how the desktop layout adapts. Every mobile-overridable setting has a checkbox next to it. When the checkbox is off, a hint shows the inherited desktop value, and the setting keeps using it. Turn the checkbox on to set a separate value for mobile. Turn it off again to fall back to the desktop value. ### Set different padding and font size per device * **Padding:** every block, column, card, and individual element (such as text, an image, or a button) has a padding control you can override for mobile. Switch the canvas to **Mobile**, turn on the checkbox next to **Padding**, and set the new value. The desktop padding stays the same until you turn the checkbox on. * **Font size:** turn on **Font size (mobile)** on a text element to set a size just for mobile. Without this override, large headlines shrink automatically to fit the phone screen. ### Control how columns stack on mobile [Columns](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#columns) layouts stack vertically on phones by default. In the mobile view, use **Stack columns** to set how each Columns block behaves: * **Stack:** columns flow into a single column, top to bottom (default). * **Keep row:** columns stay side by side, preserving the desktop layout. * **Reverse:** columns stack in reverse order. Useful for image + text rows: keep the image on the right on desktop, but show it on top on mobile. ![Mobile section in the right panel with Stack, Keep row, and Reverse options for a Columns block](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-36.webp) ### Show or hide content per device Open a block or element’s settings and use **Hide on mobile** or **Hide on desktop** under **Visibility** to show it on only one device, so you can build device-specific banners, CTAs, or navigation without maintaining two templates. ![Visibility settings with Hide on mobile and Hide on desktop checkboxes for a selected block](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-35.webp) Note Hiding content by device is not the same as hiding it by recipient. To show different blocks to different recipients, see [Show content to specific recipients](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/). ## Configure email-wide styling and footer After the main layout is in place, open the **Settings** tab to set fonts, colors, and spacing that apply across the whole email, and fill in the **Footer** with your company details and unsubscribe link. Defaults here keep new blocks consistent until you override them on the canvas. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/) ## Save your email Click **Save** in the top bar to save your changes. Use **Undo**/**Redo** in the top bar to step back and forward through your edits. Note Saving stores your email in **Content** → **Email content**. From there you can edit and manage it, and use it in your campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails) ### Restore a previous version Pushwoosh saves a snapshot every time you click **Save**. To access them, open the **⋮** menu in the top bar and select **Version history** — available once the email has been saved at least once. The left panel lists saved versions newest first, with the author, timestamp, and a **Latest** badge on the current version. Select any version to preview it on the right, then click **Restore** to load it into the editor. ![Version history panel with saved versions on the left and an email preview on the right](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-48.webp) Restoring loads the version’s HTML content, locale variants, and theme as unsaved changes. It doesn’t touch the email-wide styling and footer set in the **Settings** tab. Nothing is overwritten until you click **Save**. To undo a restore, press **Cmd/Ctrl+Z**. Pushwoosh keeps the 20 most recent versions per email, and a version waiting for approval is never evicted. Tip If your browser closes before you save, Pushwoosh keeps a local backup of your unsaved edits. The next time you open that email, a banner offers **Restore** or **Discard**. This backup exists only in the browser where it was created and is cleared as soon as you save. ## Check your email before sending Click **Check** in the top bar to open **Smart check** and scan your email for common issues: * Buttons without a link * Unfilled AI placeholders * Empty image slots and missing alt text * Text contrast that doesn’t meet WCAG AA * An empty or too-long subject line * A missing sender postal address in the footer of a marketing email * Content that pushes the email over Gmail’s clipping limit (~102 KB, after which Gmail truncates the message) * Untranslated content * [Unreviewed translations](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#review-unreviewed-translations) left unconfirmed * A countdown timer whose end date has already passed * A [display condition](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) with a broken or empty rule, a branch that lost the block it chains to, or a condition that won’t apply because dynamic content is disabled for the account * Broken [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/) syntax in the subject, preheader, footer, or body, checked in every language you added * A [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block using **Catalog rule**, **Cart**, or **Recommendations** with no [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) ![Smart check panel with a Liquid syntax error, a button link error, a missing postal address, and an empty preheader](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-40.webp) Click a finding to jump straight to the affected element on the canvas. Note Smart check runs inside the editor and doesn’t check deliverability. [Audit](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email), which runs on the campaign send step against a real test send, still checks things like spam score, broken links, and the unsubscribe header. Run Smart check first to fix editor-visible issues, then rely on Audit before you send. Smart check validates Liquid syntax automatically every time you open it — there’s no separate button to click. While the check is running, the panel shows **Checking Liquid syntax…**; Smart check doesn’t report that everything passed until this check has come back, so a slow or failed Liquid check can’t hide behind a false “all clear.” Note The Liquid check validates syntax only. It does not verify that the tags or event attributes you reference exist or that values are available for every user. ## Send a test email Before sending your email campaign, you can send a test email to preview how the content will appear in recipients’ inboxes. 1. Open the **⋮** menu in the top bar and select **Test email**. ![Test email option in the top bar's overflow menu](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-32.webp) 2. In the window that opens, add up to 50 recipients: pick one or more saved [test devices](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/), or type any valid email address to add it to the list. To remove a recipient, click the **×** on it. ![Send test email dialog with two added recipients shown as chips, each with an × to remove it](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-33.webp) 3. Optionally, check **Add [TEST] before the subject line** to prepend `[TEST]` to the subject in every language of this email content. The checkbox is off by default. 4. Click **Send** to send the test message to all chosen recipients. If every address fails, Pushwoosh shows an error in the dialog; if only some fail, it lists the failed addresses in a toast and still delivers to the rest. ## Use your saved email content Now that your email content is ready, it can be used in email campaigns. [Learn more about how to use it](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails) --- # Set up your email in the editor **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/ Follow this step-by-step guide to set up your email content, subject line, and localization in the Pushwoosh editor for effective campaigns. --- Before you begin creating email content, make sure that you set up your email. The steps for setting up the email are the same for both the [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/) and [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor). Note The language, subject, and preheader bar can collapse into a single summary row. Click the row (or focus it and press **Enter**) to expand it and edit any of the three fields. Click the row again to collapse it. The first time you open the editor, before you’ve made this choice yourself, the bar starts collapsed if the email already has a subject line, or expanded if it doesn’t. ![Collapsed summary row showing the default language, subject line, and preheader as one line above the canvas](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-9.webp) ## Define the languages you want to use in your email By default, your content is in one language (referred to as **Default** in the editor). However, if you want to customize the content for users who speak different languages, you can add additional languages from the provided list. For instance, if the majority of your database is in English, but you want to send Spanish to Spanish speakers and German to German speakers, you would set up your content language preferences as follows: default /es /de. This means that users who have opted in to receive emails by default will receive content in English, while those with Spanish or German language settings will receive content in their respective languages. To add more languages, click on the **Plus** sign. ![Language selection interface showing plus sign to add additional languages](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-1.webp) From the list of available languages, choose the languages you wish to use in your emails. Then click **Apply.** Use the language switcher above the canvas to switch between languages and edit content for each one. Content you haven’t translated yet for the current language shows dimmed. Start typing to create the translation. The default language keeps showing until you do. ![Language switcher above the canvas with dimmed untranslated content visible in the email editor](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-29.webp) Note Any block you add appears with the same content for every language. Remember to translate new text and button labels for every language you use in your email. ### Review unreviewed translations Sometimes a language shows content you didn’t type yourself — a pasted or duplicated block already carried it, or the language opened on its own because the subject line already had a translation for it. Pushwoosh highlights this content with the same dashed outline used for untranslated content; hover over it to see which case applies — unreviewed content warns that it may belong to another email. Adding a language manually runs the same check; if Pushwoosh finds matching copy already in the document, a toast reports how many texts it found. Edit each marked text to confirm it belongs in this email, or click **Reset imported copy** in the language bar to discard all unreviewed translations for that language and fall back to the default content. The marking only holds for the current editing session — reopen the email later and previously unreviewed translations show as regular content. ### Auto-translate content Click **Auto-translate** in the language bar to fill missing translations with AI instead of typing each language by hand. Auto-translate covers the full email: body text, buttons, lists, menu items, image alt text, subject line, preheader, and footer links. Translations you entered manually are not overwritten. ![Language bar with a Default language selector and an Auto-translate button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-41.webp) Note * Press **Ctrl+Z** (or **Cmd+Z**) to undo the entire operation in one step. * The default unsubscribe link text is already available in 30 languages. Auto-translate applies to a custom unsubscribe label only. * Countdown timer labels (Days, Hours, Minutes, Seconds) appear in the recipient’s language automatically. ### Import and export translations **Export** downloads a CSV of the email’s texts and translations. Use it as a starting file for a translator, or as a backup before you run Auto-translate. Export and Import cover the same content as [Auto-translate](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#auto-translate-content): body text, buttons, lists, menu items, image alt text, subject line, preheader, and footer links. ![Language bar with Auto-translate, Export, and Import buttons next to the language selector](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-89.webp) To choose which languages the CSV carries: 1. Click **Export** in the language bar. 2. In **Languages**, add or remove languages. You can pick any language, not only ones already in the email. Languages already added to the email are pre-selected, each with its current translations already filled in. 3. Click **Download CSV**. The button stays disabled until at least one language is selected. ![Export translations dialog with a language picker pre-filled from the email's languages and the Download CSV button](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-91.webp) The downloaded CSV has one row per text and one column per language you picked, named by its language code. To fill translations from a file instead of typing each language by hand: 1. Click **Import** in the language bar. 2. Choose a `.csv` or `.json` file. A CSV needs a `text` column with the default-language copy and one column per language code. JSON needs the shape `{"de": {"source text": "translation"}}`. 3. Check **Overwrite translations that are already filled** to replace translations you already have with the file’s version, or leave it unchecked to fill in only what’s missing. 4. Review the summary of how many texts the import will fill and in how many languages. 5. Click **Import**. ![Import translations dialog with a chosen CSV file, the overwrite checkbox, and a summary of texts to fill](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-90.webp) Note * A row matches a text in the email by its default-language wording, so an edited or reordered layout still matches. A row with no matching text is skipped and listed in a warning before you confirm. * Any language in the file that’s new to the email is added automatically. * A language code outside Pushwoosh’s known list is still added as given, and the dialog calls it out in its own warning before you confirm. #### If Pushwoosh can’t read the file Instead of a preview, you see one of these messages: | Message | When it happens | | ------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | The file is empty. | The file has no content. | | The file is not valid JSON. | A `.json` file (or a file starting with `{`) can’t be parsed as JSON, or its top level isn’t an object. | | The first column must be named “text”. | A CSV file’s header row names its first column something other than `text`, `source`, `default`, or `original`. | | Add a column per language next to the “text” one. | A CSV file’s header row has no column after the text column. | | No translations found in the file. | Every row is missing a language code, a text, or a translation, leaving nothing to apply. | ## Add a subject line Above the canvas, in the **Subject line** field, type the subject line for your email. ![Subject line input field in email editor](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-2.webp) If you want to add an emoji to your subject line, click the **Emoji icon** next to the **Subject line** field. You can also generate a subject line with AI: click the **AI** icon next to the field to have it written from the email content you’ve already added. ## Add a preheader Below the subject line, use the **Preheader** field to add a short summary that appears after the subject line when recipients view the email in their inbox. ![Preheader field below the subject line](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-8.webp) To add an emoji, click the **Emoji** icon next to the **Preheader** field. To generate a preheader with AI, click the **AI** icon next to the field. AI writes it from the email content you’ve already added. ## Create email content After you set languages, the subject line, and the preheader, build the email body in the Drag & drop editor. [Learn how](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) To show or hide blocks for specific recipients in the same email, set [display conditions](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/) after you add blocks to the canvas. ## Save your email Once you’ve created email content and configured the settings, click **Save** in the top bar. Before sending your campaign, you can also send a test email to check how it looks. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#save-your-email) ![Save button in the top bar of the email editor](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-6.webp) ## Manage email content After creating and saving your email, it will appear in your list of email content. ![Email content list showing saved emails ready for use in campaigns](https://docs.pushwoosh.com/emails-set-up-your-email-in-the-editor-7.webp) From there, you can effortlessly incorporate it into your customer journeys. This allows you to send automated, personalized messages and make any necessary edits. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails) --- # Show content to specific recipients **URL:** https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/show-content-to-specific-recipients/ Personalize email content per recipient in the Pushwoosh Drag & drop editor with display conditions, branch chains, and custom Liquid expressions. --- A **display condition** shows or hides a block for specific recipients based on their [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). Every subscriber gets the same email, but each one only sees the blocks meant for them. Example You tag some subscribers as `loyalty_member`. You add a block with a members-only offer and set a condition: show this block only when `loyalty_member` is `true`. Everyone else never sees it. You can add a display condition to any block or element on the canvas, except the **Footer**. **To set a display condition:** 1. Select the block or element on the canvas. 2. In the right panel under **Display condition**, turn on **Show this block conditionally** (**Show this element conditionally** if you selected an element inside a block). ![Display condition panel with the Show this block conditionally checkbox off, default always shown](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-45.webp) 3. In the first field, pick a Tag. 4. Set the operator and value (for example, **is** and **true**). ![Display condition panel with Show this element conditionally on, a loyalty_member is true rule, and Hide block selected](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-46.webp) 5. Click **+ Add rule** to add more rules. Choose **All rules** if every rule must match, or **Any rule** if at least one is enough. 6. Under **If value is unknown**, choose **Hide block** or **Show block**. This controls what happens when the Tag has no value for a recipient. When a condition is active, the block shows a chip on the canvas with a short summary of the rule: **IF** for a standalone condition, or **ELSE IF** / **ELSE** for a block in a branch chain. ## Show different content to different groups A single display condition only controls one block: the editor shows it to some recipients and hides it from others. It does not replace one block with another. Use **Branch** when different recipients should see different blocks in the same place, and each recipient should see exactly one variant. **To set up a branch chain:** 1. Place two or more blocks (or elements inside the same block) directly next to each other on the canvas. 2. On the **first block**, turn on **Show this block conditionally** (or **Show this element conditionally**) and set the Tag rule. The first block has no **Branch** setting. The editor checks this block first. 3. On **each middle block** (every block below the first except the last), turn on the display condition, set a Tag rule, and set **Branch** to **Else if**. The editor checks this block only when the conditions of the blocks above it didn’t match. 4. On the **last block**, turn on the display condition and set **Branch** to **Else**. Do not add a Tag rule. This block is the fallback for everyone who did not match any block above. Tip Think of the chain as **if → else if → else**: the first block is the first check, each **Else if** block is the next check when the previous one did not match, and **Else** is what everyone else sees. The editor checks blocks in order from top to bottom and shows the first one that matches. All other blocks in the chain stay hidden for that recipient. Note Each branch links only to the block directly above it. If you move blocks apart or set **Branch** without a valid block above, the chain breaks. [**Check your email**](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#check-your-email-before-sending) flags this as an issue. ### Example scenario: re-engagement by last purchase A re-engagement email where each recipient sees content based on how long ago they last bought something. Note `days_since_last_purchase` must be a **Number** (Integer or Price) Tag set on the recipient profile. The **≥** operator is only available for numeric Tags. | Block | Branch | Condition | What the recipient sees | | ------- | ----------- | ------------------------------------- | ---------------------------------------------- | | Block 1 | None | `days_since_last_purchase` **≥** `90` | ”We miss you” message with a win-back discount | | Block 2 | **Else if** | `days_since_last_purchase` **≥** `30` | A reminder about new arrivals | | Block 3 | **Else** | None | A regular promo block | A recipient inactive for 120 days matches both Block 1 and Block 2, but sees the win-back message because Block 1 comes first. Someone inactive for 30–89 days sees the new-arrivals reminder. Everyone else sees the regular promo block. **To set up this scenario:** 1. Add three **Text** blocks one after another as three separate blocks on the canvas. 2. **Block 1:** turn on **Show this block conditionally**. Set `days_since_last_purchase` **≥** `90`, leave **If value is unknown** on **Hide block**, and add the win-back copy. ![Display condition for Block 1 with days_since_last_purchase greater than or equal to 90 and Hide block selected](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-52.webp) 3. **Block 2:** turn on the display condition. Set `days_since_last_purchase` **≥** `30`, set **Branch** to **Else if**, and add the new-arrivals copy. ![Display condition for Block 2 with days_since_last_purchase greater than or equal to 30 and Branch set to Else if](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-53.webp) 4. **Block 3:** turn on the display condition, set **Branch** to **Else**, and add the regular promo copy. Do not add a Tag rule. ![Display condition for Block 3 with Branch set to Else and no Tag rule](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-54.webp) ## Write a custom Liquid condition When the rule builder is not enough, write the condition yourself in [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/). 1. Select the block or element on the canvas. 2. Under **Display condition**, turn on **Custom Liquid expression**. 3. Type your condition. For example, to show a block to recipients in Germany or VIP subscribers: ```plaintext Country == "DE" or VIP ``` The editor wraps your expression in a Liquid `if` tag around the block when the email is sent. A custom expression replaces any rules set in the builder above it. Tag names with spaces are converted automatically. For example, `Last Order` becomes `LastOrder`. ![Custom Liquid expression checkbox enabled with a Country equals DE or VIP condition in the input field](https://docs.pushwoosh.com/emails-drag-and-drop-email-editor-47.webp) --- # Ad inventory **URL:** https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/ Manage sponsored campaigns and creatives your account sells through email Ad slots and Sponsored push, and access permissions. --- The **Ad inventory** holds the campaigns and creatives your account sells to advertisers for [email Ad slots](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#ad-slot) and [Sponsored push](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign). You use the same list for both email and push. ![Ad inventory list with Name, Advertiser, Status, Spent, and Updated columns, plus New campaign](https://docs.pushwoosh.com/content-email-ad-inventory-1.webp) Check **Spent** on each row to see how many times the campaign has gone out so far (emails and pushes together) compared with its **Sends limit**. **Spent** counts how many times the ad was sent, not money: * `"120 of 500"` when the campaign has a limit * `"120, no limit"` when **Sends limit** is `0` * `"3 of 2 · limit reached"` when the campaign has hit its cap Note **Ad slot** must be enabled for your account. If **Content → Ad inventory** is missing, contact Support or your account manager. Assign **See email ads** so you can open this screen, and **Modify email ads** so you can create, edit, or delete on it. Without **Modify email ads**, you can only view. Those permissions cover both email Ad slots and Sponsored push. Set them under [Manage user access and permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#available-permissions). The inventory is shared across every application in your account, not limited to one. Open it from a different application and you’ll see the same campaigns and creatives. ## Add a campaign 1. Go to **Content → Ad inventory**. You can also open `https://app.pushwoosh.com/applications//email-ads`, replacing `` with your application’s code. 2. Click **New campaign**. 3. In **Name**, enter an internal name for the campaign, for example “September sponsor”. 4. In **Advertiser**, enter who bought the slot. 5. Optionally enter a **Category** tag. An Ad slot can find the campaign by this tag instead of by ID. * Choose **Category** when you want to swap sponsors in the inventory without editing the email template. * Choose **Campaign ID** in the Ad slot when you need one specific campaign. * See [Ad slot](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#ad-slot) for how both fields work together. ![New campaign form with Name, Advertiser, and Category fields filled in](https://docs.pushwoosh.com/content-email-ad-inventory-2.webp) 6. Set **Status** to one of these: * **Draft**, until the campaign has at least one creative * **Active**, when the campaign should appear in emails or pushes * **Paused** or **Finished**, to stop it (identical behavior, reversible to **Active**) 7. Under **Channels**, turn on **Email**, **Push**, or both. * Leave both off when you create the campaign to save it as email only. * Turn on **Push** if you’ll use [Sponsored push](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign). * Turn on both if the same campaign should appear in email and push. * After save, keep at least one channel on when you edit, or you cannot save. 8. Set **Sends limit** if you want a maximum number of times this campaign can go out. Leave it at `0` for no maximum. * The count includes every email and every push that uses this campaign. * When the count reaches the limit, the campaign stops being used in new sends. **Status** can stay **Active**. * Check **Spent** on the list to see how close you are, including when the limit is reached. * If both **Email** and **Push** are on, they share one limit. 9. Click **Save**. Pushwoosh keeps you on the campaign’s own page so you can add creatives next. ![New campaign form with Status, Channels, Sends limit fields, and the Save button](https://docs.pushwoosh.com/content-email-ad-inventory-3.webp) Tip To enter a **Campaign ID** in an Ad slot block, open the campaign and copy the number from the browser address bar. It’s the last part of the URL, after `/email-ads/edit/`. Or use **Category** instead and skip the ID. ## Add creatives to a campaign A campaign needs at least one creative to show anything. Keep the campaign in **Draft** until you’ve saved at least one creative. If you set **Active** with no creatives, an Ad slot can still pick the campaign, but there is nothing to show, so the slot looks the same as when no campaign was found. 1. Click **Add creative**. 2. In **Name** (required), enter an internal name for the creative. 3. In **Image URL**, paste a link to the image for the ad, or click **Choose from library** next to the field to upload one to Media storage (`.jpg`, `.jpeg`, `.png`, `.gif`, or `.webp`) and use that instead. Required if **Headline** is empty. 4. In **Headline**, enter the ad’s headline. Required if **Image URL** is empty. 5. In **Text**, enter the ad’s body copy. 6. In **Button text**, enter the button label. Leave it empty for no button. 7. In **Target URL** (required), enter the page the ad should open. ![Add creative form with Name, Image URL, Headline, Text, Button text, and Target URL filled in](https://docs.pushwoosh.com/content-email-ad-inventory-4.webp) 8. Set **Weight** if the campaign has more than one creative. * For each send, Pushwoosh uses the creative with the highest **Weight**. * Leave the field empty to use the default of 1. * Use a lower weight for a backup creative. To switch to it later, either raise its **Weight** or remove the heavier one. 9. Click **Save**. ![Add creative form's Weight field, Preview, and Save button](https://docs.pushwoosh.com/content-email-ad-inventory-5.webp) Note Every recipient of that send gets the same creative. See [Ad slot](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#ad-slot) for why. For [Sponsored push](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign), Pushwoosh first skips any creative missing **Headline** or **Text**, then uses the highest **Weight** among the rest. A picture alone is fine for email, not for push. If no creative is left, the push uses the Title, Subtitle, and message you wrote on the Sponsored push preset. ## Use the campaign After at least one creative is saved, set **Status** to **Active**. Then put the campaign into the place where recipients should see the ad: * For email, open your email in the Drag & drop editor, add an [Ad slot](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#ad-slot) block, and point it at this campaign with **Campaign ID** or **Category**. * For push, create a [Sponsored push](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign) preset and pick this campaign from the **Campaign** dropdown. Turn on the **Push** channel on the campaign first, or it won’t appear in that list. ## View a campaign’s report Open a campaign and scroll to **Report** to review each creative from the campaign’s creation date through today. ![Campaign Report with one row per creative showing Letters and sends, Opens, and Clicks](https://docs.pushwoosh.com/content-email-ad-inventory-6.webp) * Check **Letters and sends** for how many emails used this creative, and how many separate email sends that covers. Push isn’t in this line. For emails and pushes together, check **Spent** on the Ad inventory list. For push opens, use **Push taps**. * Check **Opens** for how many people opened the email, as a percentage of letters sent. * Check **Clicks** for how many people clicked the creative, how many click events that includes, and the rate as a percentage of opens. * After the creative has gone out on push, check **Push taps** for how many devices opened the sponsored push, out of all taps. If the campaign hasn’t gone out in any send yet, you’ll see “Nothing to report yet”. Note Only clicks on the creative’s own image and button count toward **Clicks**. A click on a different link in the same email, even to the same website as the ad, doesn’t change **Clicks**. **Push taps** means the person opened the notification, the same as an open for any other push. There is no separate number for whether they reached the advertiser’s page from the push. ## Manage campaigns and creatives On the Ad inventory list: * Click a campaign’s name to open it. * Click the **three-dot menu (⋮)** next to a campaign to **Edit** or **Delete** it. Deleting a campaign deletes its creatives too. * On a campaign’s page, click **Edit** or **Delete** next to a creative to change or remove it. Caution Editing or deleting a campaign or creative here affects every application in the account, not just the one you’re viewing it from. Before deleting a campaign, check Ad slots and Sponsored push presets in every application that might use it. With only **See email ads**, you can open campaigns and read their fields, but you cannot create, edit, or delete. The creatives list only shows each creative’s **Name** and **Weight**. Its **Image URL**, **Headline**, **Text**, **Button text**, and **Target URL** appear only when you open **Edit**, which needs **Modify email ads**. --- # Email templates library **URL:** https://docs.pushwoosh.com/product/content/email-content/email-templates-library/ Pick a pre-built layout from the Pushwoosh AI email builder's template gallery, organized by use case, for any campaign. --- Go to **Content** → **Email content** → **Create email content** to open **Start with a template**. For an empty canvas, choose **Blank email**. The AI email builder’s template gallery has pre-built email layouts grouped by use case. Pick one to start with your [Brand book](https://docs.pushwoosh.com/product/content/brand-book/) colors, fonts, and block defaults already applied. Then customize any part of it. Filter the gallery by category to narrow the picks, or browse **All**. Templates are listed below by that same category. ## Welcome Onboard a new user or a new trial. ![Start with a template gallery filtered to the Welcome category, showing four template tiles](https://docs.pushwoosh.com/emails-email-templates-library-14.webp) **Segment:** all newly registered users, or users who just started a trial. **Best time to send:** immediately after signup or trial start, with a checklist or follow-up touch going out within the first day. * **Welcome & onboarding:** a first-touch welcome for any new signup, introducing the product and the next steps. * **Founder’s welcome:** a personal, high-touch welcome from leadership, for products where a founder-led relationship matters. * **Onboarding checklist:** nudge users to finish setup steps they haven’t completed yet. * **Trial started:** kick off a free trial and point users to the features that prove value fastest. ## Promo Drive a sale, a discount, or a limited-time push. ![Start with a template gallery filtered to the Promo category, showing eight template tiles](https://docs.pushwoosh.com/emails-email-templates-library-15.webp) **Segment:** all users, or recent shoppers, wishlist users, and past promo responders. **Best time to send:** at the start of the promo window, closer to the deadline for single-offer or countdown formats. * **Seasonal sale:** a broad discount across categories, with a headline offer plus two product picks. * **Limited-time offer:** one big offer with a single CTA, built for a short, focused promo. * **Countdown sale:** a live countdown timer to manufacture urgency toward a deadline. * **Personal promo code:** a personalized, single-recipient discount code. * **Birthday treat:** a birthday or anniversary occasion-based perk. * **Flash sale:** a very short, high-urgency deal measured in hours. * **Referral program:** incentivize existing users to refer new ones. * **Seasonal greeting:** a goodwill, relationship-building touch with no discount attached. ## Newsletter Send a recurring digest or a one-off announcement. ![Start with a template gallery filtered to the Newsletter category, showing three template tiles](https://docs.pushwoosh.com/emails-email-templates-library-16.webp) **Segment:** subscribed users, or a specific interest segment. **Best time to send:** on a fixed recurring cadence your subscribers expect, or as soon as there’s news worth sharing. * **Newsletter digest:** a curated round-up of recent content. The stories live in a [News block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#news), so you can load them from an RSS or Atom feed instead of retyping each one. * **Announcement:** a single important update or story that deserves its own send. * **Product updates:** a changelog-style recap of what shipped and improved recently. ## Products Showcase catalog items. ![Start with a template gallery filtered to the Products category, showing four template tiles](https://docs.pushwoosh.com/emails-email-templates-library-17.webp) **Segment:** all users, or those who’ve shown interest in a specific category or product line. **Best time to send:** whenever you have new or noteworthy inventory to highlight. There’s no fixed cadence. * **Product offer:** promote a specific set of products alongside a discount code. * **Product catalog:** a browse-style showcase across multiple categories. * **New arrival:** announce a single new product launch. * **Bestsellers:** a social-proof push featuring your top-selling items. ## Cart Bring a shopper back to a saved or watched item. ![Start with a template gallery filtered to the Cart category, showing three template tiles](https://docs.pushwoosh.com/emails-email-templates-library-18.webp) **Segment:** users who added items to their cart or wishlist, or watched a specific product, without completing a purchase. **Best time to send:** within 1–2 hours of the triggering action for cart abandonment, and as soon as stock or price changes for the rest. * **Abandoned cart:** remind a shopper about items left in their cart. * **Back in stock:** alert a shopper that a previously unavailable item is available again. * **Price drop:** notify a shopper that an item on their wishlist got cheaper. ## Transactional Confirm an order, appointment, shipment, or account action. ![Start with a template gallery filtered to the Transactional category, showing four template tiles](https://docs.pushwoosh.com/emails-email-templates-library-19.webp) **Segment:** the specific user who triggered the action, not a broader marketing segment. **Best time to send:** immediately after the triggering action, since the confirmation itself is the message. * **Order confirmation:** confirm a completed purchase and share order and tracking details. * **Appointment reminder:** remind a user of an upcoming booked appointment. * **Shipping confirmation:** notify a user their order has shipped, with tracking. * **Password reset:** deliver a secure reset link after a user requests one. ## Events Invite, remind, or follow up on an event. ![Start with a template gallery filtered to the Events category, showing three template tiles](https://docs.pushwoosh.com/emails-email-templates-library-20.webp) **Segment:** registered or invited attendees for a specific event. **Best time to send:** the invitation ahead of the event, a reminder shortly before it starts, and a thank-you shortly after it ends. * **Event invitation:** invite users to an upcoming event and get them to register. * **Webinar reminder:** nudge registered attendees right before a webinar starts. * **Event thank-you:** follow up after an event with a recording and materials. ## Win-back Re-engage an inactive user or ask for feedback. ![Start with a template gallery filtered to the Win-back category, showing two template tiles](https://docs.pushwoosh.com/emails-email-templates-library-21.webp) **Segment:** users who haven’t engaged in a while, or anyone you want direct feedback from. **Best time to send:** after a defined period of inactivity (for example, 30–60 days) for win-back, or anytime for feedback requests. * **Win-back:** re-engage a user who’s gone quiet with a reason to come back. * **Feedback request:** ask a user for quick feedback on their experience. --- # HTML code editor **URL:** https://docs.pushwoosh.com/product/content/email-content/html-code-editor/ Build your email content from scratch with HTML code editor. Create custom emails with localization, merge tags, and reusable content blocks. --- [Youtube Video: Build your email content from scratch with HTML](https://youtube.com/watch?v=DryThwJHYxk) If you are proficient in HTML and prefer to write your own code, you can create emails using the Pushwoosh HTML code editor. Note Don’t have coding skills? Use the Drag & drop editor to effortlessly create email content. Zero coding skills required. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) Important Before you start creating email content, it’s essential to properly set up your email within the email editor you choose. This involves configuring languages, specifying the subject line, providing sender information, and more. [Learn how to do it](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor) ## Create email content To begin creating email content, paste your HTML code into the HTML tab. The editor provides real-time email previews as you are working on them. [](https://docs.pushwoosh.com/emails-html-code-editor-1.webm "HTML code editor interface showing real-time email preview as code is typed") Alternatively, you can upload an HTML file by clicking on **Upload HTML File** located in the bottom panel. To add a personalized touch to your emails, use custom tags. For this: 1. Click on the **Tag** icon located at the bottom of the editor. 2. Select the desired Tag, and its modifier, and provide a default value if needed. 3. Click **Insert** to include the custom tag in your email content. ![Custom tag insertion interface showing tag selection and default value options](https://docs.pushwoosh.com/emails-html-code-editor-2.webp) To include an emoji in your email, click the **Emoji** icon located at the bottom of the editor. ### Add images To add an image to your email, include an `` tag in your HTML and set the `src` attribute to the image URL. To reuse an image from your [Media storage](https://docs.pushwoosh.com/product/content/media-store/), copy its URL there and paste it into the `src` attribute. See [Copy an image URL](https://docs.pushwoosh.com/product/content/media-store/#copy-an-image-url) for the steps. ```plaintext Summer sale banner ``` Replace `YOUR-MEDIA-STORAGE-URL` with the URL you copied from the Media storage. ## Use localization Localization empowers you to deliver personalized experiences to users in different languages. In the HTML code editor, you can use localization by defining a default language and adding multiple language options. For instance, you can set a default message in English and then include translations in German and Spanish. This ensures that your email content adapts to each user’s language preferences. Below is an example of how to structure your localization data. This uses English as the Default language and German and Spanish translations. ```json { "default": { "button": "Shop now", "description": "For a limited time, you can enjoy a 20% discount on all our premium coffee blends", "subtitle": "Don't miss it", "title": "☕ Coffee Promotion Alert!" }, "de": { "button": "Jetzt einkaufen", "description": "Für kurze Zeit erhalten Sie einen Rabatt von 20% auf alle unsere Premium-Kaffeemischungen", "subtitle": "Verpassen Sie es nicht", "title": "☕ Kaffee-Promotion Benachrichtigung!" }, "es": { "button": "Comprar ahora", "description": "Por tiempo limitado, puedes disfrutar de un descuento del 20% en todas nuestras mezclas de café premium", "subtitle": "No te lo pierdas", "title": "☕ ¡Alerta de Promoción de Café!" } } ``` Once your localization data is structured and added in the **Localization Tab** of the editor, incorporate it into your HTML content using placeholders. The placeholders dynamically insert localized text into your content, based on the user’s language preference. Here’s how to implement it in your HTML ```plaintext {{title|text|}} {{subtitle|text|}} {{description|text|}} {{button|text|}} ``` Consider this scenario: you’re advertising a limited-time discount on coffee blends. By using the localization structure mentioned above, you can communicate your promotional message effectively to users speaking different languages. Your HTML code for the promotion might look like this: ```html ☕ Coffee Promotion Alert

{{title|text|}}

{{description|text|}}

{{button|text|}}
``` With this setup, if a user’s language is German, they’ll see the email in German. Users who opted to receive emails in English will see it in English. Those who opted to receive it in Spanish will receive it in Spanish. This ensures that all recipients get your message in their preferred language. [](https://docs.pushwoosh.com/emails-html-code-editor-3.webm "Localization demonstration showing email content in different languages") ## Insert and combine email content blocks Pushwoosh allows you to combine email content by inserting one piece of email content into another. This makes it easy to reuse elements like headers, footers, or specific content blocks across different emails. For example, if you have **Email content A** (a header) and **Email content B** (a newsletter), you can insert **Email content A** into **Email content B** without manually copying content each time. ### Syntax To insert one piece of content into another, use the following syntax: ```plaintext {% email_content "AAAAA-BBBBB" %} ``` Where “AAAAA-BBBBB” is the ID of the email content you want to insert. You can find the ID located beneath the content name in your list of email content. ![Email content ID display showing content name and unique identifier](https://docs.pushwoosh.com/emails-html-code-editor-4.webp) Important * You can insert content recursively, meaning one content block can include another, up to a maximum depth of 3 levels. For example: * **Content A** can insert **Content B**. * **Content B** can insert **Content C**. * However, **Content C** cannot insert another block. * **Cyclical insertions** (e.g., where **Content A** inserts **Content B** and **Content B** tries to insert **Content A**) are not allowed and will result in an error. **Example** Let’s say you have two pieces of content: * **Header content** (ID: “AAAAA-BBBBB”) with a predefined header design. * **Newsletter content**, where you want to include the header. To insert the header into the newsletter, you would use the following: ```plaintext {% email_content "AAAAA-BBBBB" %} ``` This allows you to easily reuse a predefined header in multiple email campaigns. It saves time and ensures consistency across your emails. ## Add an unsubscribe link Include an unsubscribe link in your email so recipients can opt out in line with regulations and their preferences. Pushwoosh provides three variables you can use in your HTML. ### Unsubscribe link Add this link so users can opt out from the message they received: ```plaintext Unsubscribe ``` The variable is replaced with the Pushwoosh unsubscribe URL when you send the email. * When the subscription preference center is enabled, recipients are unsubscribed only from the category assigned to that email (e.g., *Newsletter*). Depending on your unsubscribe flow, they may first confirm the unsubscribe and answer a short survey. On the final page, they can **Resubscribe** to that category or **Manage preferences** to adjust all email categories. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#what-happens-when-a-user-clicks-the-unsubscribe-link) * When the subscription preference center is not enabled, recipients unsubscribe from all marketing emails. Clicks are counted in the unsubscribe rate for that message in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history). ### Unsubscribe from all categories Add a link that unsubscribes the user from all email categories at once: ```plaintext Unsubscribe from all emails ``` Note To let you deliver important emails to your customers, we do not delete unsubscribed users from your user base. For users who have fully opted out (for example, via the default unsubscribe link when the preference center is not enabled, or via the Unsubscribe from all link), the [**Unsubscribed Emails** default tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags) is set to ‘true’. Consider creating a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of users who did not unsubscribe (tag value ‘false’) to continue messaging them. To avoid complaints, do not email users who have unsubscribed. ### Preference center link Add a direct link to the [preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#preference-center-link) where users manage which categories they receive: ```plaintext Manage preferences ``` [Learn how the preference center works](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#how-the-preference-center-works) The screenshot below shows a footer that uses all three link types. ![Email footer example with all three unsubscribe links: Unsubscribe, Manage preferences, Unsubscribe from all emails](https://docs.pushwoosh.com/emails-html-code-editor-6.webp) Caution If you use your own unsubscribe link instead of the Pushwoosh variables, users who opt out via your link are not included in the unsubscribe rate in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history). Only users who followed the Pushwoosh link are counted in the stats for that email. ## Save email content After you have finished creating your email, click on the **Save** button located at the top of the editor. ![Save email content dialog showing name and label fields](https://docs.pushwoosh.com/emails-html-code-editor-5.webp) In the new window that appears, give your email a clear name, which could be the same as your Subject line. You can add a label afterward from the [email content list](https://docs.pushwoosh.com/product/content/email-content/#view-and-manage-your-email-content) to help you easily find it. If you haven’t [set up sender details](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor) yet, make sure to do that as well. Then, click **Save.** Now that your email content is ready, it can be used in email campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails) ## Send a test email Before sending your email campaign, you can send a test email to preview how the content will appear in recipients’ inboxes. This allows you to review the layout, content, and any personalized elements to ensure accuracy before finalizing the campaign. If needed, adjust the content based on the test results. Note If KYC **(Know Your Customer)** is not completed, you can still send test emails by verifying a test email address. To do this, go to the [Test Devices](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/) section, add the email address you want to use for testing, and complete the verification process. To send a test email, click **Test email** in the email editor. ![Test email button in email editor interface](https://docs.pushwoosh.com/shared-14.webp) In the window that opens: 1. In the **Email address** field, enter the email address where you want to receive the test email. If KYC has not been completed, you will need to select a verified email address from the list of test addresses instead of entering an email. ![Verified test email address selection dropdown](https://docs.pushwoosh.com/shared-50.webp) 2. If your email contains dynamic content (placeholders for personalized data), add sample values for testing purposes. For example: * **Age (integer)**: Enter a number to represent the age placeholder (e.g., `21`). * **FirstName (string)**: Enter a sample first name (e.g., `David`). These values will replace actual recipient data in the test email, allowing you to verify how the dynamic content appears. 3. Optionally, check **Add [TEST] before the subject line** to prepend `[TEST]` to the subject in every language of this email content. The checkbox is off by default. 4. Once you’ve filled in the necessary details, click **Send test email** to send the test message to the provided email address. ![Send test email confirmation dialog with sample data fields](https://docs.pushwoosh.com/shared-34.webp) --- # Reuse email content blocks across campaigns **URL:** https://docs.pushwoosh.com/product/content/email-content/reuse-email-content-blocks/ Easily reuse email content blocks across campaigns by inserting one email content into another using Pushwoosh syntax. --- Pushwoosh allows you to combine email content by inserting one piece of email content into another. This makes it easy to reuse elements like headers, footers, or specific content blocks across different emails. For example, if you have **Email content A** (a header) and **Email content B** (a newsletter), you can insert **Email content A** into **Email content B**. This eliminates the need to manually copy content each time. Note Reusing a single block *within* the Drag & drop editor works differently. See [Keep copies in sync](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#keep-copies-in-sync). Turning that on links every copy automatically, without you writing this syntax yourself. ## Syntax To insert one piece of content into another, use the following syntax: ```plaintext {% email_content "AAAAA-BBBBB" %} ``` Where “AAAAA-BBBBB” is the ID of the email content you want to insert. You can find the ID located beneath the content name in your list of email content. Important *** You can insert content recursively, meaning one content block can include another, up to a maximum depth of 3 levels. For example: * **Content A** can insert **Content B**. * **Content B** can insert **Content C**. * However, **Content C** cannot insert another block. **Cyclical insertions** (e.g., where **Content A** inserts **Content B** and **Content B** tries to insert **Content A**) are not allowed and will result in an error. ## Example Let’s say you have two pieces of content: * **Header content** (ID: “AAAAA-BBBBB”) with a predefined header design. * **Newsletter content**, where you want to include the header. To insert the header into the newsletter, you would use the following: ```plaintext {% email_content "AAAAA-BBBBB" %} ``` This allows you to easily reuse a predefined header in multiple email campaigns, saving time and ensuring consistency across your emails. --- # How to create and manage in-app content **URL:** https://docs.pushwoosh.com/product/content/in-apps/ Create and manage in-app templates in Pushwoosh using the built-in editor, native editor, custom ZIP files, or JSON config. --- Important Before you begin creating in-app content, make sure that you set up the in-app channel and integrated the SDK. To send in-app messages to your users (one-time, in a Customer Journey, or from a push), see the channel setup and sending options. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) Pushwoosh provides several flexible options for creating in-app templates, so you can choose the method that best fits your skills and needs. Go to **Content** → **In-Apps** and click **Create in-app**. The button opens the built-in editor directly. Click the arrow next to it to build a template a different way instead. You can also create a new template inline while adding an in-app element to a Customer Journey or campaign: #### Built-in in-app editor **Create in-app** opens the built-in editor by default. It’s a no-code, visual builder that lets you create in-app messages using content blocks, images, buttons, and forms. Perfect for marketers and non-technical users. [Create in-app messages in the built-in editor ](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) #### Native in-app editor Click the arrow next to **Create in-app** and choose **Native in-app** for a no-code way to build a native, SDK-rendered layout in the Control Panel: modal, fullscreen, stories, sheet, carousel, banner, or video. Loads faster and animates more smoothly than an HTML-based in-app. [Create native in-app messages ](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/) #### Upload custom template (ZIP) Click the arrow next to **Create in-app** and choose **Upload ZIP** to incorporate pre-designed in-app pages by uploading them as ZIP files. Your development team creates them using the correct syntax. For tracking button clicks, link clicks, and form submits, see [Monitor performance of custom Rich Media](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax#monitor-performance-of-custom-rich-media). [Learn Rich media templates syntax ](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/) Note To customize in-app appearance and animation styles, your development team can use the [Rich media customization guide](https://docs.pushwoosh.com/developer/guides/content/customizing-rich-media-appearance). #### Native templates via config Not available from **Create in-app**. Your development team writes the native layout directly as JSON config instead, without building it in the Control Panel. Use this for layouts the Control Panel editor doesn’t offer: pip, scratch card, and spin wheel. [Learn Native in-app templates syntax ](https://docs.pushwoosh.com/developer/guides/content/native-in-app-templates-syntax/) ## Push permission popup guides Step-by-step guides for specific in-app use cases: * **First ask (pre-permission):** show an in-app before the one-shot system prompt, and open that dialog from the accept button (iOS and Android, one HTML template). [Show an in-app before the push permission prompt](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/) * **Opt-out recovery only:** re-engage users who already disabled push or never granted it. Not for the first ask. [Create opt-out recovery popups](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/) For position and animation options, see [Configure how the in-app looks on device](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) in the built-in editor guide. ## View and manage your in-app content Note In-app content is application-specific, so it stays well-organized and simple to manage. Pushwoosh keeps your in-app templates in one place, making it easy to edit, reuse, or clone them for future campaigns. To view your in-app content, navigate to **Content** → **In-Apps**. Here, you’ll find all the in-app templates you’ve created, organized for easy access. ![Content In-Apps page in tile view with the search bar and tile/table view toggle, showing in-app templates with a type badge and a kebab menu for template actions on each card](https://docs.pushwoosh.com/content-in-apps-1.webp) Use the toggle next to the search bar to switch between **tile view** (shown by default, with a live preview per template) and **table view** (a compact list). Each template shows its **type** — the editor it was built in (**Editor**, **HTML**, or **Native · **) — and when it was last **Updated**. Templates that were never edited after creation show **Created** with the creation date instead. Here you can: * **Preview:** hover over the eye icon to preview a template. * **Statistics:** open per-template stats (impressions, interactions, audience). [Learn more about in-app statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) * **Edit…:** open the template in the editor. You can also click the template name. Templates from the previous editor open here automatically, with the design converted for the new editor. * **Clone…:** create a copy of the template. * **Delete:** remove the template. To delete several templates at once: 1. Click **Bulk actions** → **Delete…**. 2. Select the templates. 3. Click the **Delete** button above the list. Templates used in in-app messages, presets, or journeys are skipped. Messages already sent with a deleted template open without it, and its statistics are no longer available. Deletion can’t be undone. ## Use your in-app templates in campaigns To use your existing in-app templates in campaigns: * **Customer Journey:** add an In-app element and choose your template. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/) * **One-time in-app:** send a single in-app to your audience. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) * **Push that opens an in-app:** send a push that opens an in-app when the user taps. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push#define-what-happens-when-the-push-is-clicked) --- # Create native in-app messages **URL:** https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/ Build native SDK-rendered in-app messages in the Control Panel. Pick a display type, fill in content and colors, and add buttons. --- Native in-apps use ready-made layouts that the Pushwoosh SDK renders directly on the device, instead of the HTML page a custom in-app loads. This makes them load faster and animate more smoothly, and you don’t need design or development skills to build one. Reach for a native in-app when you want to launch a promotion, reminder, or feature announcement quickly and a fully custom layout isn’t necessary. If you need full control over the design, build an [HTML-based in-app](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) instead. ## Prerequisites Developer assistance needed You can build a native in-app template in the Control Panel without any code changes. But users only see it if their app runs a Pushwoosh SDK version new enough to render it. Check with your development team before you launch. Before you create a native in-app template, make sure: * The Pushwoosh SDK is integrated in your app so in-app messages can appear. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) * Your app runs iOS SDK 7.2.0+ or Android SDK 6.10.0+ for **modal**, **fullscreen**, and **stories**. **Sheet**, **carousel**, and **banner** need iOS SDK 7.2.1+ or Android SDK 6.10.1+. **Video** needs iOS SDK 7.2.0+ or Android SDK 6.11.0+. ### Cross-platform frameworks The native in-app module ships differently depending on your stack: * Flutter bundles the native in-app module automatically. No extra setup is needed beyond the Pushwoosh Flutter plugin. * React Native, Cordova, Capacitor, Unity, and Expo don’t bundle the native in-app module. Add it to your app’s underlying Android/iOS project yourself: `pushwoosh-inapp-ui` on Android, `PushwooshInApp` on iOS. See your framework’s [integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks/) for how native modules are added to your project. * Web isn’t supported. Native in-apps render through the Android and iOS SDKs, and the Web SDK has no native in-app module. ## Create a native in-app template 1. Go to **Content** → **In-apps**. 2. Click the arrow next to **Create in-app** and choose **Native in-app**. The native editor opens directly. 3. Enter a template name. 4. In **Display type** at the top of the editor, pick a display type: **modal**, **fullscreen**, **stories**, **sheet**, **carousel**, **banner**, or **video**. See [Configure the display type](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-the-display-type) for what each type needs. The editor shows a live mobile preview on the right and the template fields on the left, grouped into **Content**, **Config**, and **Actions**. What you see in each group depends on the display type you picked. ### Configure the display type Caution The **Display type** dropdown at the top of the editor lets you switch types at any time, but switching resets all the Content, Config, and Actions fields below to their defaults for the new type. Fill in content only after you’ve settled on a type. #### Modal A centered card, optionally shown over a dimmed background. 1. Under **Content**, add a title and message, each with its own text color, plus an optional image. See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Under **Config**, set the card background color. This fills the card behind your text and image. 3. Turn **Close button** on if you want a ✕ control so users can dismiss the in-app without tapping a button. 4. Turn **Dim background** on to darken the screen behind the card and keep focus on your message. 5. Under **Actions**, add one or more buttons. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons). ![Native in-app editor for the modal display type with Content, Config, and Actions fields, and a live mobile preview](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-2.webp) #### Fullscreen An edge-to-edge cover image with text and buttons on top. Fill in these fields on the left panel: 1. Under **Content**, add a cover image, plus a title and message, each with its own text color. See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Under **Config**, set the cover color. It shows while the cover image loads, or on its own if you leave the image empty. 3. Turn **Close button** on if you want a ✕ control so users can dismiss the in-app without tapping a button. 4. Under **Actions**, add one or more buttons. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons). ![Native in-app editor for the fullscreen display type with cover image, title, message, and cover color fields](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-3.webp) #### Stories A sequence of full-screen slides with progress bars at the top, similar to social media stories. Each slide is its own **Stories** entry under **Content**. 1. Fill in the first slide’s **Image**, **Title**, and **Message** (each with its own text color). See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Set **Seconds** to how long the slide stays on screen before advancing. The default is 5. Enter a value from 1 to 30. Values outside this range block **Save** with a validation error. 3. Optionally, click **Add** inside the slide to give it its own CTA button. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons). ![Stories slide with Image, Title, Message, and Seconds fields, and a nested Buttons entry with its own Add link](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-7.webp) 4. Click **Add** below the last slide to add another slide, then repeat steps 1-3 for it. 5. Under **Config**, turn **Loop** on if you want the story to start again from the first slide after the last one finishes. 6. Turn **Close button** on if you want a ✕ control so users can exit before all slides play through. ![Config panel for the stories display type with Loop and Close button toggles](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-4.webp) ##### Example of how the finished story looks for the user [](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-9.webm "Mobile preview cycling through three finished stories slides: New arrivals, Best sellers, and Free shipping") #### Sheet A bottom sheet that slides up from the lower edge of the screen. A handle at the top lets users drag it down to dismiss. Fill in these fields on the left panel: 1. Under **Content**, add a title and message, each with its own text color, plus an optional image. See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Under **Config**, set the sheet background color. This fills the panel behind your text and image. 3. Turn **Close button** on if you want a ✕ control so users can dismiss the sheet without tapping a button. 4. Under **Actions**, add one or more buttons. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons). ![Native in-app editor for the sheet display type with Content, Config, and Actions fields, and a live mobile preview](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-10.webp) #### Carousel A full-screen message with several cards users swipe through. Dots at the bottom show which card is active. Under **Content**, each card is a separate **Cards** entry. 1. Fill in the first card’s **Image**, **Title**, and **Message** (each with its own text color). See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Set **Action** for the card. Choose **close** to dismiss the in-app when the user taps the card, or **url** to open a link (same options as [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons)). 3. Click **Add** below the last card to add another one, then repeat steps 1-2 for it. 4. Turn **Close button** on if you want a ✕ control so users can leave the carousel without tapping a card. ![Native in-app editor for the carousel display type with two Cards entries and a live mobile preview showing pagination dots](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-11.webp) Caution If you turn off **Close button** and set every card’s **Action** to **url**, the user has no on-screen control to dismiss the carousel. A tap always opens a link instead of closing it. #### Banner A compact bar pinned to the top or bottom edge of the screen, for messages that shouldn’t block the whole UI. 1. Under **Content**, add a title and message, each with its own text color, plus an optional small image. See [Add an image](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-an-image). 2. Under **Config**, set **Position** to **top** or **bottom** to pin the banner at the screen edge you want. 3. Set **Auto-dismiss, sec** to close the banner automatically after that many seconds. Leave it empty to keep the banner until the user dismisses it. 4. Set the background color for the bar. 5. Turn **Close button** on if you want a ✕ control on the banner. 6. Under **Actions**, set **Action** to **close** or **url**. Banners don’t have separate buttons. Tapping anywhere on the banner runs this action. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons) for **close** and **url** behavior. ![Native in-app editor for the banner display type with Position, Auto-dismiss, and Action fields, and a live mobile preview pinned to the bottom](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-12.webp) Caution If you turn off **Close button**, set **Action** to **url**, and leave **Auto-dismiss** empty, the banner stays on screen with no way to dismiss it. A tap always opens the link instead of closing it. #### Video A full-screen player for an HLS streaming link (`.m3u8`) or a direct `.mp4` file, with an optional title, message, and buttons on top. Note Video needs iOS SDK 7.2.0+ or Android SDK 6.11.0+. On an older Android SDK, a template using this display type doesn’t render. See [Platform support](https://docs.pushwoosh.com/developer/guides/content/native-in-app-templates-syntax/#platform-support) for details. 1. Under **Content**, set the **Video URL** (required) to an absolute HTTPS link to an `.m3u8` or `.mp4` file. Optionally, add a **Poster** image shown before playback starts, and a **Fallback image** shown if the video can’t play. 2. Add a title and message, each with its own text color. 3. Under **Config**, turn **Loop** on to restart the video after it ends. 4. Turn **Muted** on to start playback with the sound off. 5. Turn **Close button** on if you want a ✕ control so users can dismiss the in-app without waiting for the video to end. 6. Under **Actions**, add one or more buttons. See [Configure buttons](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#configure-buttons). ![Native in-app editor for the video display type with Content, Config, and Actions fields, and a live mobile preview](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-14.webp) ### Configure text fields Text fields don’t support multi-line text. If you press Enter, the editor may show a line break, but Pushwoosh saves a space instead. On the device, the text appears as one line. To add an emoji, click **Emoji** next to the field toolbar. Emojis are supported in the same text fields as plain text. Check the live preview to see how they look on the layout. #### Add dynamic content Every text field supports personalization with tags and User ID. 1. Click **Personalization** next to the field toolbar. 2. Under **Tag**, choose a tag or **User ID**. 3. Choose a format modifier (for example, **CapitalizeFirst** or **regular**). 4. Optionally, enter a **Default Tag Value**. 5. Click **Insert**. ![Message field with a first name tag chip, Personalization and Emoji controls, and a panel for Tag, format modifier, and default tag value](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-13.webp) The inserted tag shows as a chip in the field. You can change the tag, modifier, and default before you click **Insert**. Personalization uses the same tags, modifiers, and defaults as [Dynamic content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). Tip For conditional logic (for example, show this text only if a tag equals a specific value), type [Liquid syntax](https://docs.pushwoosh.com/product/personalization/liquid-templates/) directly into the field. Liquid works only when a journey shows the in-app and Liquid in in-apps is enabled for your account. See [Liquid in in-apps](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/). When you click **Save**, Pushwoosh checks the dynamic content and Liquid in every text field. If a field doesn’t parse (for example, an unclosed `{% if %}` tag), **Save** is blocked and a **Dynamic content contains errors** toast names the broken field and language. Fix it there, then click **Save** again. Note The Liquid check on Save catches syntax errors like an unclosed tag. It may still pass incomplete expressions that only fail when the message is sent. Send yourself a test if you use anything beyond a simple tag. ### Configure buttons Modal, fullscreen, each stories slide, sheet, and video use the same button fields: * **Button text** and its color. * **Background**, **Border color**, and **Border radius**. * **Action:** choose what happens when the user taps the button: * **close:** dismisses the in-app. * **url:** opens the link you enter in the field that appears. Use a web address (for example, `https://example.com`) or a deep link into your app (for example, `myapp://product/123`). ![Actions panel with a Buttons entry showing Button text, Color, Background, Border color, Border radius, and Action fields](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-6.webp) Click **Add** to add another button, or the **×** on a button to remove it. Caution If you turn off **Close button** and don’t add any button either, the user has no on-screen control to dismiss the in-app. ### Add an image Every image field (image, cover image, and each card or slide’s image) works the same way. Paste a hosted image URL directly into the field, or click **Choose from Media storage** next to it to open the [Media storage](https://docs.pushwoosh.com/product/content/media-store/) and pick an image you’ve already uploaded. Picking an image from the Media storage replaces whatever URL is currently in the field. #### Add a barcode Next to **Choose from Media storage**, click **Build a barcode** to open a drawer that generates a scannable Code 128, EAN-13, EAN-8, UPC-A, or QR code and writes the resulting URL into the field: ![Image field in the Content section with the Build a barcode icon highlighted next to Choose from Media storage](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-15.webp) * **Code**. The value to encode: either a fixed code, like `SAVE20`, or a device tag, like `{Coupon|String|}` or `{LoyaltyCardNumber|String|}`, so each recipient sees their own code. A device tag is filled in with the recipient’s real value right on their device, just before the in-app appears. Defaults to `{Coupon|String|}`. ![Barcode heading with the Code field set to the Coupon device tag and its helper text](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-16.webp) * **Symbology**. Choose the barcode format: Code 128, EAN-13, EAN-8, and UPC-A are standard retail barcodes read by handheld scanners, and QR is a square code that any phone camera can scan. * **Error correction**. Appears only after you set Symbology to QR. Real-world QR codes can get scratched, blurry, or partly covered by a logo, and error correction is extra information baked into the code so it still scans despite that damage. Choose how much damage it can tolerate: `L`, `M`, `Q`, or `H`, from least to most robust. Higher levels tolerate more damage but produce a denser, busier-looking code. Defaults to `M`. ![Symbology dropdown set to QR with the Error correction dropdown that appears below it, set to M](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-17.webp) * **Print the code in digits**. Show the value under the code. ![Print the code in digits toggle switched on, with the sample QR code and its printed PUSHWOOSH-SAMPLE-CODE text below it](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-18.webp) When **Code** is a device tag, the preview substitutes a sample value, since it can’t resolve a real recipient’s tag. A fixed code, like `SAVE20`, previews exactly as entered. Click **Insert into the image field** to copy the generated URL into the **Image** field above. ![Native in-app mobile preview card showing the barcode after it was inserted into the Image field](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-20.webp) ### Add localizations By default, a native in-app has one language. To add more: Note The language selector here only counts devices on platforms that render in-app content. Language values set through user-level Tags aren’t tied to a device platform, so they don’t show up here. 1. Click **+** next to the language selector in the top bar. 2. Select the languages to add and click **Apply**. ![Select languages dialog with a search field, Select all, Deselect all, and Reuse last selection links, and a language checklist](https://docs.pushwoosh.com/rich-media-create-native-in-app-messages-5.webp) 3. Use the language selector in the top bar to switch between languages and fill in the text fields for each one. Note When you add a language, Pushwoosh copies the default language’s content into it: text, images, and button labels. Switch to that language and edit any of these fields independently. ## Save the template Click **Save** in the top right to persist your changes. The template then appears in **Content** → [**In-apps**](https://docs.pushwoosh.com/product/content/in-apps/) alongside your other in-app templates. ## Use it in a campaign A native in-app template appears in the template picker together with your other in-app templates. You can use it to: * [Send an instant in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) * Add an in-app step to a [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/) * [Open an in-app when the user taps a push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push#define-what-happens-when-the-push-is-clicked) Note Delivery settings, such as start and end dates and frequency capping, work the same as for any other in-app and aren’t part of this editor. --- # Create opt-out recovery popups **URL:** https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/ Use the no-code in-app editor to build a popup that re-engages users who declined or never granted push notification permission. --- In-app messages can win back users who unsubscribed from push notifications, whether they declined the system prompt or never granted it. Re-engaging these users can help improve overall user retention and increase their lifetime value to your business. For the first ask before the system push permission prompt (and to open that dialog from the accept button), see [Show an in-app before the push permission prompt](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/) instead. [Pushwoosh In-App Editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) allows you to create in-apps easily, without developer and designer skills required. You can add headers, texts, images (including AI-generated pictures and stock photos), buttons, forms, and other elements to the canvas and customize them in just a few clicks. You can incorporate in-app messages in an omnichannel customer journey to get the most out of your user communications. ## Build a recovery popup template Here’s an example of a popup that asks users to turn notifications back on: ![Mobile preview of a recovery popup asking users to turn notifications back on, with a benefits list and Allow button](https://docs.pushwoosh.com/in-app-message-templates-create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor-1.webp) [Create a part-screen popup in the In-App Editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device), then return here to automate when it appears in your app. ## Automate the display of your in-app message in Customer Journey Builder ### Opt-out recovery popup We recommend creating a customer journey for a segment of users who have disabled or never enabled notifications in the app. First, build a [Segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of users who have alerts disabled. Filter on the default **Push Alerts Enabled** [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) (the SDK sets it automatically from the device’s system notification permission) and set the operator to **is false**: ![Screenshot showing segment creation with Push Alerts Enabled tag filter set to false](https://docs.pushwoosh.com/in-app-message-templates-create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor-3.webp) After that, you can build a customer journey starting with the [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). Select the segment you created and set **Entry schedule** → **Recurring entry** → **Periodically** (e.g. once a month): ![Audience-based entry with segment selection and Entry schedule (Recurring entry)](https://docs.pushwoosh.com/journey-elements-audience-based-entry-6.webp) You can also add the [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step with the default [PW_ApplicationOpen](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_applicationopen) Event as the trigger so that the popup will appear once a user opens the app. As a result, the journey will look like this: ![Screenshot showing complete opt-out recovery journey with audience-based entry, wait for trigger, and in-app message](https://docs.pushwoosh.com/in-app-message-templates-create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor-5.webp) Every month, Pushwoosh will launch this journey for a segment of users who have notifications turned off. ## More useful articles and insights * [Popular in-app formats and best examples in Pushwoosh Blog](https://blog.pushwoosh.com/blog/what-are-in-apps/) * [Tips on using in-apps within your omnichannel strategy](https://blog.pushwoosh.com/blog/push-notifications-in-app-messaging/) * [In-App Editor documentation](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) --- # Get started with the no-code in-app editor **URL:** https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/ Create in-app templates without coding in the built-in editor. Add blocks, display conditions, and template styling. --- Use this guide to go from an empty canvas to a saved in-app template: create the template, build the layout, personalize it, style it, then save. Follow the steps below, or jump to a topic when you need it. ## Get started with the no-code in-app editor 1. **Create a template** An in-app template is a reusable design you build once and show in campaigns. Create one in the built-in editor from scratch, or open an existing template to customize it. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) 2. **Build the layout** Drag content blocks onto the canvas, such as headings, images, buttons, timers, products, and more. Personalize copy with Merge Tags, or use [display conditions](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/) to show different blocks to different users. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#add-content-blocks) 3. **Style the template and configure device behavior** Set document, color, and typography defaults for the whole template. Open **In-app settings** to configure position, animations, and the close button for how the message appears on device. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/) 4. **Save the template** Save the template in the editor. It appears in **Content** → **In-apps**, ready to preview, edit, or use in campaigns. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#save-the-template) ## Apply consistent branding Save your logo, colors, fonts, and block defaults once as a **Brand book**, then apply them to any in-app template in one click from the built-in editor’s **⋮** menu. [Create a Brand book ](https://docs.pushwoosh.com/product/content/brand-book/) ## Use your saved template After you save, your template appears in **Content** → **In-apps**. From there you can preview, edit, clone, or delete templates. [View and manage your in-app content](https://docs.pushwoosh.com/product/content/in-apps/#view-and-manage-your-in-app-content) ## Use your in-app templates in campaigns To use your existing in-app templates in campaigns: * **Customer Journey:** add an In-app element and choose your template. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/) * **One-time in-app:** send a single in-app to your audience. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) * **Push that opens an in-app:** send a push that opens an in-app when the user taps. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push#define-what-happens-when-the-push-is-clicked) --- # In-app editor blocks reference **URL:** https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/ Settings reference for every built-in in-app editor block, including headings, buttons, barcode, HTML, timers, and products. --- Reference for every block you can add on the [built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) canvas. Each block type serves a different purpose in the message: * **[Heading](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#heading-and-text):** titles and short headlines * **[Text](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#heading-and-text):** body copy and longer descriptions * **[Button](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#button):** a tappable call to action * **[Image](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#image):** pictures from [Media storage](https://docs.pushwoosh.com/product/content/media-store/), upload, or URL * **[Video](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#video):** a tappable video thumbnail that opens a YouTube or Vimeo video * **[Card](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#card):** an image with heading, text, and button in one layout * **[Divider](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#divider):** a line that separates sections * **[Columns](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#columns):** side-by-side layouts (2, 3, or 4 equal columns, or 33/67 and 67/33 splits) * **[Timer](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#countdown-timer):** a countdown for offers and deadlines * **[Barcode](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#barcode):** a scannable Code 128, EAN-13, EAN-8, UPC-A, or QR code generated from a personalization value, such as a voucher * **[Menu](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#menu):** a row or column of navigation or contact links * **[Social](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#social):** icons that link to your social profiles * **[HTML](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#html):** custom HTML, such as a form, survey, or embedded video * **[Products](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#products):** a grid of product cards from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) or a user’s abandoned cart ## How to configure editor blocks Drag a block onto the canvas from the **Blocks** tab, or click it to add it at the end. Select the block to open its settings in the right panel and configure content, links, size, and spacing. Tip Every block also has **Visibility** and **Display condition** settings. Use them to hide a block on phone or desktop, or show it only to specific users. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/) ### Heading and text **Heading** and **Text** blocks hold the copy users read in the in-app message. Edit the text on the canvas and format it with the toolbar. Click a **Heading** or **Text** block to edit its copy on the canvas. Select some text to bring up the formatting toolbar: bold, italic, underline, alignment, a link button, and a **text style** dropdown. ![Heading block selected on the canvas with the text formatting toolbar showing style and formatting options](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-8.webp) Merge Tags are placeholders that insert a [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) value from the user’s profile into the message, so each subscriber sees personalized copy. To add one, select the text, click **Merge Tags** in the toolbar, and choose a Tag from your account. [Learn more about personalized in-app messages](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message/) ### Image An **Image** block displays a picture in the in-app template. ![Image block on the canvas with Image settings showing Replace image, Image URL, Alt text, Generate alt with AI, Prompt, Corner radius, Alignment, and Size & fit](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-40.webp) Select it on the canvas to open the **Image** settings in the right panel. * **Add image** / **Replace image:** click **Add image** when the block is empty, or **Replace image** when a picture is already set. You can upload a file or pick one from your [Media storage](https://docs.pushwoosh.com/product/content/media-store/). * **Image URL:** paste a hosted link to the image. * **Alt text:** describe the image for accessibility. Click **Generate alt with AI** to have it written from the picture. * **Prompt:** the AI generation prompt for an image you created with **Generate with AI**. Leave it empty when you upload or link the picture yourself. * **Corner radius (px):** round the image’s corners. Leave it empty to keep the template’s default radius. Turn on **More Options** to set each corner separately. * **Alignment:** left, center, or right. **Size & fit** * **Width:** set the image’s width in px, or as a percentage of the available width. * **Height (px):** set a fixed height. Leave it empty for the image to size automatically. Below these fields, a hint shows the size the image renders at and the size to upload: 2× the rendered size, so the picture stays sharp on phone screens. If the uploaded file is smaller than the rendered size on either axis, a warning tells you it will be upscaled and look pixelated. * **Crop:** keep **Original** proportions, or pick a ratio to crop the image to that box. * **Fit:** choose **Cover** to fill the box and crop to it, or **Contain** to fit the whole image inside the box. In **Link**, set an **Action Type** (for example **Open Website** or **URL or Deeplink**) and its **URL** to open something when the image is tapped, and a **Link target** to open it in the same tab or a new one. In **Spacing**, turn on **Padding** to add space around the image. Use **All sides** for the same padding on every side, or turn on **More options** to set each side separately. #### Recommended image sizes Upload an image at 2× the size the hint under **Width** / **Height (px)** shows for that block. That’s the number to check, since it reflects your own **Width**/**Height** and crop settings. When you haven’t set a block yet, use these common slot widths as a starting point: | Where the image sits | Renders at | Upload at least | | ------------------------------------- | ------------------------------------ | ----------------- | | Full-width image in the default popup | up to 600 px wide | 1200 px wide | | Phone preview | 360–430 px wide, depending on device | up to 860 px wide | | Tablet/desktop preview | 768 px wide | 1536 px wide | Aspect ratio is up to you. The layout is responsive, so height follows whatever **Height (px)** or crop you set. See [Document and popup size](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#document-and-popup-size) to change the default popup width. Note This table covers Image blocks in the built-in editor. [Native in-apps](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/) (modal, fullscreen, stories, sheet, carousel, banner, video) don’t have a recommended cover size yet. ### Video A **Video** block adds a YouTube or Vimeo video to the message as a tappable thumbnail. The message shows a still image with a ▶ overlay. Tapping it opens the video in the YouTube or Vimeo app or browser. ![Video block on the canvas with Video settings showing Video URL, Alt text, Replace thumbnail, Thumbnail URL, and Play button overlay](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-38.webp) Select the block on the canvas to open its settings: * **Video URL:** paste a YouTube or Vimeo link. The editor fetches a thumbnail automatically. * **Alt text:** describe the video for accessibility. * **Thumbnail URL:** keep the auto-fetched thumbnail, or click **Add thumbnail** / **Replace thumbnail** to pick one from [Media storage](https://docs.pushwoosh.com/product/content/media-store/), or paste a hosted link directly. * **Play button overlay:** on by default. Turn off to show the plain thumbnail without the ▶ overlay. * **Width (px)**, **Corner radius (px)**, and **Alignment:** control how the thumbnail looks and where it sits in the layout. ### Card A **Card** block pairs an image with a heading, text, and button in a single ready-made layout, so you don’t have to place and align four separate blocks by hand. ![Card block on the canvas with Card settings showing Layout, Replace image, Image URL, Alt text, Prompt, Padding, and Corner radius](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-37.webp) * **Layout:** choose which side the image sits on relative to the content, or turn on **Wrap text around image**. * **Image:** add or replace the image, set alt text, edit the AI generation **Prompt**, add **Padding**, and set **Corner radius (px)**. * **Size & fit:** set the image’s **Width**, **Height**, **Crop** (Original or a fixed ratio), and **Fit** (Cover or a focal point). The exact fields depend on the **Layout** you picked. * **Link:** make the whole card open a URL when tapped. Edit the card’s heading, text, and button the same way you edit the standalone [Heading, Text](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#heading-and-text), and [Button](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#button) blocks. ### Button A **Button** is a tappable call to action in the in-app message. Use it to open a URL, run custom JavaScript, or close the message. Click a **Button** block to edit its label on the canvas. Use the toolbar to change font size, text color, bold, italic, underline, and alignment. To set what happens when the user taps the button: 1. Click the link icon in the toolbar to open **Insert/Edit Link**. 2. Choose an **Action Type**: **Open Website**, **URL or Deeplink**, **Custom Javascript**, or **Close In-App**. 3. Fill in the fields for that action (see below). 4. Click **Save**. #### Open Website **Open Website** opens a web page when the user taps the button. Enter the address in **URL**, for example `https://example.com`. ![Insert Edit Link dialog for a Button with Action type Open Website and a URL field](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-10.webp) #### URL or Deeplink **URL or Deeplink** opens a link or deep link when the user taps the button. Enter the address in **URL**, for example an app screen: `myapp://screen`. ![Button toolbar with Insert Edit Link dialog open, Action type URL or Deeplink, and URL set to myapp://screen](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-45.webp) #### Close In-App **Close In-App** closes the in-app message when the user taps the button. It has no fields to fill in: choose it and click **Save**. Use it for a dismiss button such as **Not now** or **No thanks**. ![Insert Edit Link dialog for a Button with Action Type Close In-App and no other fields](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-62.webp) **Close In-App** is available for buttons only. To close the message and do something else on the same tap, such as save a tag or send an event, use [Custom Javascript](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#run-custom-javascript) instead. #### Run custom JavaScript **Custom Javascript** runs code when the user taps the button. Enter the code in the **onClick** field. Use it when a tap should do something in Pushwoosh, not only open a link. The person in the app still sees a normal button. They do not see tags or events. You decide what happens next on screen: leave the message open, or close it in the same **onClick**. ![Insert Edit Link dialog for a Button with Action Type Custom Javascript and onClick that sends an event and closes the in-app](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-11.webp) **Available methods in onClick** * `pushwoosh.closeInApp()`: close the in-app message. Omit it to keep the message open after the tap. If closing is the only thing the button does, use [Close In-App](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#close-in-app). * `pushwoosh.sendTags({...})`: save values as [device tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) for later campaigns. * `pushwoosh.postEvent(...)`: send a [custom event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/), for example to start a Customer Journey. * `pushwoosh.getTags(...)`: read current tags. Useful in custom HTML to change what the message shows. * `pushwoosh.getCustomData()`: read custom data passed from the push that opened this in-app. * `pushwoosh.registerForPushNotifications()`: trigger the platform’s native push permission dialog, for example from a [pre-permission in-app](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/). ##### Example: let users pick an interest and save it for later campaigns 1. Add a question in a **Text** or **Heading** block, for example: “Which sport are you into?” 2. Add one **Button** per option, for example: **Yoga** and **Gym**. ![In-app template with Yoga and Gym buttons and Custom Javascript onClick for the Yoga button](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-12.webp) 3. Set **Action Type** to **Custom Javascript** for each button. 4. For the **Yoga** button, paste this into **onClick**: ```js pushwoosh.sendTags({ interest: "yoga" }); pushwoosh.closeInApp(); ``` 5. For the **Gym** button, paste this into **onClick**: ```js pushwoosh.sendTags({ interest: "gym" }); pushwoosh.closeInApp(); ``` When the user taps **Yoga**, Pushwoosh writes the `interest` tag on their device and closes the in-app. You can then target that user in a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) or [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/). [Learn more about how to create in-apps with JavaScript ](https://docs.pushwoosh.com/developer/guides/messaging-channels/inapp-with-javascript/) ### Divider A **Divider** is a horizontal line between content blocks. Use it to separate sections visually in the template. Configure **line style** (Solid, Dashed, Dotted), **color**, **thickness**, and **width** in the settings panel. ### HTML The **HTML** block lets you paste custom HTML into an in-app template. Use it for a custom form, survey, or embedded video when there is no dedicated Form block for in-app messages. In an in-app shown by a journey’s In-App element, the HTML block can also hold [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/) that fills in each user’s Tags and event attributes. In-apps from **Content** > **In-Apps** show Liquid as written. Liquid in in-apps is off by default. To enable it for your account, contact your Customer Success Manager or support. ##### Examples The examples below show common ways to use the HTML block in an in-app template: a lead form, a survey, and an embedded video. Note The form and survey examples save device tags and send events through Pushwoosh. Create the custom events in your account before you use them in campaigns. * Lead form This lead form writes name and email as tags, sends a `promo_requested` event, shows a thank-you message, then closes the in-app. ![HTML lead form in the in-app editor with name and email fields and a Send me the code button](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-13.webp) HTML code ```html

Get your 20% discount

Leave your name and email. We'll send the promo code right away.

🎉 Done! Check your inbox for the promo code.
``` * Survey This survey saves the selected workout time as a tag, sends a `schedule_survey_completed` event, shows a thank-you message, then closes the in-app. ![HTML survey in the in-app editor asking when the user prefers to work out with four time options](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-14.webp) HTML code ```html

When do you prefer to work out?

Help us schedule the best classes for you

Got it! We'll send you the best schedule 💪
``` * Video This example embeds a YouTube video in the in-app message. HTML code ```html In-App Video Example

In-App Video Example

``` ### Menu The **Menu** block places several clickable items in a row or column. Use it for a navigation bar or a list of company contacts. For each menu item, set: * **Label:** the text users see * **URL:** where the item opens * **Target:** same tab or a new tab In the block settings, you can also set: * **Direction:** Horizontal or Vertical * **Alignment:** Left, Center, or Right * **Separator:** an optional divider between items * Font, size, weight, letter spacing, and link/text colors ### Social The **Social** block displays a row of social media icons that link to your profiles. Use it to send users to Facebook, Instagram, X, and other platforms from the in-app message. Add platform entries, then set alignment, icon size, and the gap between icons. ![Social block settings with TikTok, Instagram, and Facebook links plus Alignment, Icon size, and Gap](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-15.webp) ### Countdown timer The **Timer** block is a countdown in the in-app message. Use it for limited-time offers, flash sales, and event reminders. To set it up: 1. In **Countdown**, set **End date** and **End time**, then choose the **Timezone** and **Language** for the timer labels. 2. Turn **Labels** on to show “days / hours / minutes / seconds” captions under the digits, or off for numbers only. 3. In **Appearance**, set **Background**, **Digits color**, **Labels color**, and the font size for digits (default 40px) and labels (default 16px). 4. Adjust **Spacing** (padding) as needed. ![Timer block settings with Countdown end date and time, Labels, Appearance, and Spacing options](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-16.webp) ### Barcode The **Barcode** block renders a scannable barcode or QR code from a personalization value, most often a [voucher](https://docs.pushwoosh.com/product/content/vouchers/#display-a-voucher-as-a-barcode). To set it up: 1. In **Code**, keep the device tag `{Coupon|String|}`, or click **Merge tag** and choose another device tag that stores each recipient’s code. The SDK fills the tag on the device before the in-app is shown. This differs from the email Barcode block, which uses `{{voucher}}`. ![Barcode Code field with Merge tag button and device-tag helper text](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-58.webp) 2. Choose **Symbology**: * **QR:** a square 2D code phones can scan. Accepts any text, including alphanumeric codes like `WELCOME2025-A1` * **Code 128:** a linear barcode for any text, including the same alphanumeric codes * **EAN-13:** the common retail barcode in Europe and many other regions. Digits only, 12–13 characters * **EAN-8:** a shorter retail barcode for small packaging. Digits only, 7–8 characters * **UPC-A:** the common retail barcode in North America. Digits only, 11–12 characters 3. If you chose **QR**, set **Error correction**. This adds spare data so the code can still scan when part of it is covered, worn, or hard to see. Choose **L**, **M**, **Q**, or **H** (default **M**). Higher levels survive more damage but make the pattern denser, which can be harder to read at small sizes. Skip this field for other symbologies. 4. Optionally turn on **Print the code in digits** to show the value under the code. ![Barcode Symbology set to QR, Error correction M, and Print the code in digits checked](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-56.webp) 5. In **Appearance**, set **Bars color** darker than **Background** — most scanners can’t read light bars on a dark background. Set **Width (px)** and **Height (px)** to at least 600×200 so the code stays scannable at typical phone-camera range. Leave **Quiet zone, modules** on **Auto** for the default margin. ![Barcode Appearance settings with Bars color, Background, Width, Height, and Quiet zone](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-57.webp) Note On the canvas the block shows a sample barcode, not the real recipient code. A device tag like `{Coupon|String|}` is filled in only on the device, so [Preview](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#preview) can’t build the real image either. Check the barcode on a real device before you publish. Use **EAN-13**, **EAN-8**, or **UPC-A** only for a tag value that’s digits only, at the length each expects. For alphanumeric codes like `WELCOME2025-A1`, use **Code 128** or **QR** instead. ### Columns **Columns** split the canvas into side-by-side sections so you can place content next to each other in one row. Drag one of these blocks onto the canvas: * **2 columns:** two equal columns * **3 columns:** three equal columns * **4 columns:** four equal columns * **33 / 67:** left column about 33% wide, right about 67% * **67 / 33:** left column about 67% wide, right about 33% Then add content to each column. To change spacing for all columns in the template, open **Settings** → **Columns defaults** and adjust outer padding, column padding, and the gap between columns. ### Products A **Products** block shows a grid of product cards from one shared template. Each card can include an image, title, description, price, and a buy button. The template repeats for every product. ![Products block with two product cards showing image, title, description, price, and Shop now button](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-47.webp) In the settings panel, under **Source**, choose where the products come from: * **Manual:** enter products by hand, or click **Pick from catalog…** to select specific items from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) by ID. You can mix both in the same block. * **Catalog rule:** pull products automatically by category and sort order, resolved fresh from the catalog when the message is shown. * **Cart:** show the items from each user’s current abandoned cart. [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/) Note **Catalog rule** and **Cart** need **Content Templates** on your plan, and an [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) (**Settings → API Access**). If the **Source** selector won’t switch to one of them, or the block doesn’t render products, contact Support or your account manager, or create a token and re-select the source. Once you’ve picked a source, these settings control how the grid and its cards look: * **Number of products** (or **Max products shown** for **Cart**): how many cards to show. * **Columns:** 1, 2, or 3 cards per row. * **Gap:** the space between cards. * **Description limit:** the character limit description text is trimmed to, so cards stay a consistent size. * **Card template:** pick a layout, then set **Card background**, **Card corner radius**, and **Content padding** for every card. ## Save and reuse blocks Save a block to reuse it in this or other in-app templates instead of rebuilding it each time. 1. Select the block on the canvas. 2. Click the bookmark icon (**Save block for reuse**) in the block’s toolbar and give it a name. ![Card block toolbar with Move up, Move down, Duplicate, Delete, Save block for reuse, and Ask AI icons](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-42.webp) Saved blocks appear under **Saved blocks** at the bottom of the **Blocks** tab. * Drag a saved block onto the canvas, or click it to add it at the end of the template. * Click the **×** on a saved block’s tile to delete it. ### Keep copies in sync When you save a block, turn on **Keep copies in sync** to link every inserted copy to the same source. Edit the source once, and the change applies everywhere that block is used. A synced copy is locked on the canvas. You can’t edit it directly. Select the copy to open its settings: * **Edit synced block:** unlock this copy so you can change it. A banner appears at the top. Click **Apply to all** to save your changes to the source and every other copy, or **Cancel** to discard them. You can unlock only one synced block at a time. * **Update from source:** replace this copy with the latest version from the source. * **Detach from source:** unlink this copy from the source so you can edit it on its own. Note Each time you open an in-app template, Pushwoosh checks its synced copies and updates any that are out of date. A toast shows how many were refreshed. If the source has newer content, the copy’s settings show a warning until you reopen the template or click **Update from source**. If the source was deleted, the copy keeps its last content and you can only **Detach from source**. --- # Configure in-app template settings **URL:** https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/ Set template-wide document, color, and typography defaults, and configure position, animations, and the close button on device. --- These settings apply to the whole in-app template at once, rather than a single block. Open or create a template in the [built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) first (**Content** → **In-apps**), then use **Settings** and **In-app settings** as described below. ## Configure template-wide styling Template settings control styling for the whole in-app template: document size and background, popup look, and default styles for blocks. Open the **Settings** tab in the right panel. ![Collapsed Settings sections from Document through Divider defaults in the right panel](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-17.webp) Start with shared look, then size, then defaults for new blocks: 1. Set **Color scheme** and **Typography** so colors and fonts stay consistent across the template. 2. Set **Document** and **Popup** for size, margins, background, and how the message sits on the dimmed backdrop. 3. Use the defaults sections only when you want new blocks to match the template automatically. On the canvas, change a single block when that piece should look different. Defaults apply wherever an individual block does not override them. To clear style overrides on the template and return to the defaults, use **Reset custom styles** in the top bar **⋮** menu. See [Create in-app messages in the built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/). ### Document and popup size Use **Document** to control the message size and canvas spacing. Use **Popup** to control how the message sits on the dimmed backdrop. There’s no height setting. The popup grows with its content. #### Document In **Document**, set the padding, a background image, the gap between blocks, and a default corner radius for images. To change how wide the popup looks on the phone, use these two fields in **Document**. **Max width (px)** is the widest the popup is allowed to be, in pixels. On a large screen the popup will not grow past this number. **Side gutters (px)** sits right under **Padding**. It sets empty space on the left and right of the popup. One value applies to both sides. Change this field when you want the popup to look wider or narrower: * Raise the number to leave more empty space and make the popup narrower. * Lower the number to make the popup wider. * Enter a negative number and the editor treats it as 0. ![Document open with Max width, Padding, Side gutters at 0 px, and the effective width hint](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-59.webp) New templates start at 0 px gutters, so the popup fills the screen width. Check the hint under **Max width** to see how wide the popup will be with your current values. #### Popup In **Popup**, set the corner radius and overlay color for how the message sits on the dimmed backdrop. ![Popup settings with corner radius and overlay color in the Settings panel](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-60.webp) ### Color scheme and typography In **Color scheme**, set named color roles (heading, accent, links, background, borders, and more). Those roles update colors across the template at once. Existing blocks keep colors you already set on those blocks. In **Typography**, set the default font family and sizing for text in the template. ### Dark mode variant **Dark mode variant** lets you set a second set of colors for when the template shows in dark mode. 1. In **Color scheme**, turn on **Dark mode variant**. 2. Set the separate colors for dark mode. 3. Use the **sun/moon icon** in the top bar to check light vs. dark while you edit. This canvas toggle isn’t the same as the read-only **Preview** panel. Until you set your own dark colors, the toggle can derive them automatically. See [Create in-app messages in the built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/). ![Color scheme section with Dark mode variant enabled and dark color roles in Settings](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-61.webp) Note **Dark mode variant** shows on iOS and web only. On Android, users always see the light colors. Older templates saved before **Dark mode variant** existed only have light colors, until you open and re-save them. Background and cover images that already have a color baked into the file don’t switch for dark mode. If you need that, use a transparent image, or provide a separate image for dark mode. ### Block and layout defaults Use these sections so new content matches the template until you style a block on the canvas: * **Block defaults:** shared starting styles for blocks you add later. * **Columns defaults:** spacing for every Columns layout (outer padding, padding inside each column, and the gap between columns). * **Card defaults:** starting look for Card containers that group content in the layout. * **Button group defaults:** spacing and alignment when several buttons sit together as a group. * **Button defaults:** starting look for new Button blocks so CTAs stay consistent across the template. * **Divider defaults:** starting line style, color, thickness, and width for new Divider blocks. When you finish in **Settings**, click **Save** in the top bar to keep the template changes. ## Configure how the in-app looks on device **In-app settings** control how the message appears and behaves on the device: position, animations, swipe gestures, and the close button. Position, animations, and swipe apply on iOS and Android Native. The close button style is different. It is stored with this template and shows on every platform that displays the message. Click **In-app settings** in the top bar. ![In-app settings button in the editor top bar between Undo Redo and Save](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-18.webp) ### Position on screen Choose **Fullscreen**, **Top**, **Center**, or **Bottom**. For a full-screen message, also set **Max width** in **Document** as described under [Document](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#document). ![In-app settings Position on Screen options Fullscreen Top Center Bottom](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-48.webp) ### Animations * **Present Animation** / **Close Animation:** None, Fade, Left, Right, Up, or Down. Choose **Default** to use the same open/close motion your app already uses for in-apps. * **Animation Duration (MS):** 100 to 3000 milliseconds. Leave empty to keep your app’s usual timing. ![In-app settings Present Animation Close Animation and Animation Duration fields](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-49.webp) Note **None** and **Fade** currently work on Android. On iOS, the message appears and disappears instantly. ### Swipe to close Enable any combination of Left, Right, Up, and Down. ![In-app settings Swipe to Close with Left Right Up and Down options](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-50.webp) ### Close button Turn **Show close button** on or off. When it’s on, set: * **Position:** Top right, Top left, Bottom right, or Bottom left. * **Background:** a color or **Transparent**. * **Corner radius:** rounds the corners of the close button background. Available only when **Background** is a color (not **Transparent**). Set 0–16 px. 16 px makes a circle (default). 0 px makes a square. * **Icon Color:** color of the close icon. * **Margin:** space around the close button on each side (**Top**, **Right**, **Bottom**, **Left**). ![In-app settings Show close button with Position Background Corner radius Icon Color and Margin](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-51.webp) Unlike position, animations, and swipe, the close button style is stored with this template and shows on every platform that displays the message, not only on iOS and Android Native. 1. Click **Apply** to save your **In-app settings** choices, or **Cancel** to discard them. 2. Open **Preview** from the top bar **⋮** menu to check position, animations, and the close button before you save. 3. Click **Save** in the top bar to store the template. --- # Create in-app messages in the built-in editor **URL:** https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/ Create in-app templates without coding in the built-in editor. Add images, buttons, and personalized content for campaigns. --- Build an in-app message template in the Pushwoosh built-in editor without writing code. You get a reusable layout with content blocks, images, buttons, and personalized text that you can show in a Customer Journey, as a one-time in-app, or from a push. Note Before you start, integrate the Pushwoosh SDK so in-app messages can appear in your app. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) ## Create a new in-app template An in-app template is a reusable design you create once and then show to users in campaigns. You can create one in the built-in editor, upload a custom HTML ZIP, or build a native rich media template. This article covers creating a template from scratch in the built-in editor. Follow the same steps to customize an existing template. 1. Go to **Content** → **In-apps**. 2. Click **Create in-app**. The built-in editor opens directly. ![Content In-apps page with the Create in-app button in the top right of the templates list](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-1.webp) Click the arrow next to **Create in-app** to build a template a different way: * **Upload ZIP:** [upload a ZIP file](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax) with your HTML in-app message. * **Native in-app:** build an SDK-rendered message using ready-made layouts. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/) ## Explore the editor layout The built-in editor has four areas. Each one has a clear job. ### Top bar The top bar is where you manage the template as a whole: its name, undo history, save status, how it looks on device (**In-app settings**), languages (**Auto-translate** and the language switcher), phone or desktop preview width (phone offers **360**, **390**, **393**, or **430 px**, matching common device widths), and a light/dark canvas toggle (**sun/moon icon**). See [Configure how the in-app looks on device](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) and [Localize the template](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#localize-the-template). ![Top bar of the in-app editor with template name, Undo, Redo, In-app settings, language switcher, Auto-translate, phone and desktop preview, light/dark toggle, and Saved](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-3.webp) Open the **⋮** menu in the top bar for more actions: ![Expanded ⋮ menu in the in-app editor top bar with Preview, Check, Brand book, Download .zip, Apply brand book styles, and Reset custom styles](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-54.webp) #### Preview **Preview** lets you try the message before you save it: tap buttons, move through multi-screen flows, and check layout on phone or desktop. ![Preview panel with phone and desktop toggle, light and dark toggle, Restart, and an in-app message showing image, offer text, timer, and Claim button](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-44.webp) What works in preview: * Buttons and multi-screen flows run for real. * Links and deep links are reported, not opened. * SDK calls and open/click statistics stay off. * The language matches the language switcher. Merge Tags stay as placeholders. In the preview header, switch between phone and desktop, and between light and dark, to see all the resulting layouts. Click **Restart** to show the message from the beginning again. To leave preview, press Esc or click outside the panel. #### Check **Check** opens **Smart check** and scans the template for common issues. Smart check looks for: * Buttons, menu items, and image links without a URL, or with a URL that isn’t an http(s), `mailto:`, `tel:`, or deep link (for example `myapp://promo`) address * Unfinished AI placeholder blocks * Empty image or video slots and missing alt text * Product cards with a missing or invalid link * Text contrast that doesn’t meet WCAG AA * Untranslated content * A countdown timer whose end date has already passed * A [display condition](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#show-blocks-to-specific-users) with a broken or empty rule, a branch that lost the block it chains to, or a condition that won’t apply because dynamic content is disabled for the account ![Smart check panel listing a missing button link error and an empty image slot warning](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-53.webp) Click a finding to jump straight to the affected element on the canvas. If nothing is wrong, the panel shows **All checks passed — the content looks ready to save.** #### Brand book * **Brand book:** open this application’s [Brand book](https://docs.pushwoosh.com/product/content/brand-book/). * **Apply brand book styles:** restyle the template to match your Brand book (once you’ve set one up). * **Reset custom styles:** remove style overrides on the template and return to the defaults. #### Download .zip **Download .zip** saves the template as a ZIP file. It downloads the last saved version, so save your changes first. The menu item appears only after the template has been saved at least once — new, unsaved templates don’t show it. ### Canvas Edit the message on the canvas: click text, images, and buttons to change them. Use the phone/desktop toggle in the top bar to check how the layout looks at each width. The toggle only changes the preview, not the layout itself. Use the **sun/moon icon** in the top bar to keep editing the canvas while it renders in light or dark. It works even without a **Dark mode variant** set up — dark colors are derived automatically until you set your own. This is a live editing view, not the read-only **Preview** panel. ### Layers The Layers panel lists every block in your template as a tree, in the same order as on the canvas. Open it from the left edge to select a nested block (for example, a button inside a group) or drag rows to reorder content. ![Layers panel listing Card, Image, Heading, Text, Timer, Buttons, and Footer in a tree](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-5.webp) ### Right panel Switch between three tabs: * **Blocks:** add content to the message. See [In-app editor blocks reference](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/). * **Settings:** apply styling to the whole template at once. See [Configure in-app template settings](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/). * **AI:** chat with the AI assistant to generate or edit content from a prompt. ![Blocks tab in the right panel with Heading, Text, Button, Image, and other content blocks](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-6.webp) ## Add content blocks 1. Open the **Blocks** tab in the right panel. 2. Drag a block onto the canvas, or click a block to add it at the end of the template. 3. Keep adding blocks until the layout is ready. ![Empty in-app editor with Start with a block on the canvas and the Blocks panel open](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-7.webp) Each block type serves a different purpose in the message: * **[Heading](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#heading-and-text):** titles and short headlines * **[Text](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#heading-and-text):** body copy and longer descriptions * **[Button](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#button):** a tappable call to action * **[Image](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#image):** pictures from [Media storage](https://docs.pushwoosh.com/product/content/media-store/), upload, or URL * **[Video](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#video):** a tappable video thumbnail that opens a YouTube or Vimeo video * **[Card](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#card):** an image with heading, text, and button in one layout * **[Divider](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#divider):** a line that separates sections * **[Columns](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#columns):** side-by-side layouts (2, 3, or 4 equal columns, or 33/67 and 67/33 splits) * **[Timer](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#countdown-timer):** a countdown for offers and deadlines * **[Menu](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#menu):** a row or column of navigation or contact links * **[Social](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#social):** icons that link to your social profiles * **[HTML](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#html):** custom HTML, such as a form, survey, or embedded video * **[Products](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#products):** a grid of product cards from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) or a user’s abandoned cart See the [In-app editor blocks reference](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/) for settings for each block, and [how to save and reuse blocks](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#save-and-reuse-blocks). ### Show blocks to specific users Not every user needs to see the same blocks. Set a **display condition** on a block so it shows or hides based on the user’s [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). You still send one template, but each person only sees the parts that apply to them. Select the block on the canvas, open **Display condition** in the right panel, and turn on **Show this block conditionally**. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/) ## Localize the template A multi-language template stores localized copy for each language in one in-app design. For how multi-language messaging works across channels, see [Multi-language messaging](https://docs.pushwoosh.com/product/personalization/multi-language/). Note The language selector here only counts devices on platforms that render in-app content. Language values set through user-level Tags aren’t tied to a device platform, so they don’t show up here. To set up multi-language content: 1. Click the language switcher in the top bar to open the language list. 2. Select the languages you want to add, then click **Apply**. 3. Use the language switcher to move between languages and edit each block’s content for the selected language. 4. Click **Auto-translate** to fill in missing translations automatically — review AI-generated text before saving. Note Any block you add appears with the same content for every language. Remember to translate new text and button labels for every language you use in your template. ## Configure template settings Use two places to style and place the message: * **Settings** tab (right panel): document size, colors, typography, and default styles for blocks. * **In-app settings** (top bar): position on screen, animations, and the close button. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/) ## Save the template Click **Save** in the top bar to save your changes. Use **Undo**/**Redo** in the top bar to step back and forward through your edits. Note Saving stores your in-app template in **Content** → **In-apps**. From there you can preview, edit, and manage templates. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/#view-and-manage-your-in-app-content) ## Update translations from your own systems Your development team can update a saved template’s translations through the [Pushwoosh API](https://docs.pushwoosh.com/developer/api-reference/) or an AI client connected to the [ManyMoney AI MCP server](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/), without resending the template’s HTML or design. This only works on a template that has already been saved once (see [Save the template](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#save-the-template) above). A template with no saved content has nothing to merge translations into. The update merges only the languages and text keys sent in the `localization` field. Every other language, key, and the visual design stay as saved. * Sending a key with no value removes that key from its language. * Sending a language with no value removes the whole language from the template. * The default language can switch to any language left in the template after the update. * The update is rejected if it would remove every language from the template, or if the requested default language isn’t one of the languages left after the merge. For this, you’ll need assistance from your development team. --- # Show in-app content to specific users **URL:** https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/ Show or hide in-app blocks per user with Visibility and Display condition settings, including custom Liquid expressions. --- Every block in the [built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/), and every element inside it, has two settings in the right panel for controlling who sees it: **Visibility** and **Display condition**. ## Show or hide content per device Open a block or element’s settings and use **Hide on mobile** or **Hide on desktop** under **Visibility** to show it at only one preview width, matching the phone (360, 390, 393, or 430 px) and tablet (768px) widths of the [phone/desktop toggle](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#top-bar) in the top bar. Note Hiding content by preview width is not the same as hiding it by user. To show different blocks to different users, see [Show content to specific users](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/#show-content-to-specific-users) below. ## Show content to specific users A **display condition** shows or hides a block for specific users based on their [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). Every user gets the same in-app template, but each one only sees the blocks meant for them. Example You tag some users as `loyalty_member`. You add a block with a members-only offer and set a condition: show this block only when `loyalty_member` is `true`. Everyone else never sees it. You can add a display condition to any block or element on the canvas. **To set a display condition:** 1. Select the block or element on the canvas. 2. In the right panel under **Display condition**, turn on **Show this block conditionally** (**Show this element conditionally** if you selected an element inside a block). 3. Pick a Tag, an operator, and a value (for example, **is** and **true**). 4. Click **+ Add rule** to add more rules. 5. Under **If value is unknown**, choose **Hide block** or **Show block**. This controls what happens when the Tag has no value for a user. ![Visibility and Display condition settings with Hide on mobile, Hide on desktop, a display condition rule, and Custom Liquid expression](https://docs.pushwoosh.com/rich-media-create-rich-media-in-the-built-in-editor-41.webp) ### Example scenario: win-back offer for lapsed users A win-back offer that only appears for users who haven’t purchased in a while, so active shoppers don’t see a discount they don’t need. Note `RFM Recency` must be a **Number** Tag on the user profile (days since last order). The **≥** operator is only available for numeric Tags. | Condition | What the user sees | | ------------------------ | ---------------------------------------------- | | `RFM Recency` **≥** `90` | The win-back Card block, with a discount offer | | Anything else | The Card block stays hidden | **To set up this scenario:** 1. Add a **Card** block with the win-back offer (image, heading, and a discount code button). 2. Select the Card on the canvas, and under **Display condition**, turn on **Show this block conditionally**. 3. Set the rule to `RFM Recency` **≥** `90`. 4. Under **If value is unknown**, choose **Hide block** — new users without purchase history shouldn’t see a win-back message meant for lapsed ones. A user who last ordered 120 days ago sees the offer. A user who ordered last week, or has no order history yet, never sees the block. ## Write a custom Liquid condition When the rule builder is not enough, write the condition yourself in [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/). 1. Select the block or element on the canvas. 2. Under **Display condition**, turn on **Custom Liquid expression**. 3. Type your condition. For example, to show a block to users in Germany or VIP subscribers: ```plaintext Country == "DE" or VIP ``` A custom expression replaces any rules set in the builder above it. Tag names with spaces are converted automatically. For example, `Last Order` becomes `LastOrder`. --- # Show an in-app before the push permission prompt **URL:** https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/ Build a cross-platform pre-permission in-app in the Control Panel, trigger it by event, and segment users who still lack push. --- A pre-permission in-app is a message you show before the system push permission prompt. Use it to explain why push notifications are useful, then ask for permission only after the user says yes in your in-app. On iOS, the system prompt appears only once per install. If the user taps **Don’t Allow**, the app can’t show that prompt again. Push stays off until they turn notifications on in Settings. Asking in-app first means you spend that one shot only on users who’ve already said yes to you. Use this guide if you want to design one cross-platform HTML prompt in the Control Panel and trigger it by event on both iOS and Android. Use a different guide if: * Your team will ship an iOS-only prompt in app code (not editable in the Control Panel, not started by a Journey event): [iOS push primer](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-primer/). * You need to re-engage users who already disabled push or never granted it (recovery only, not the first ask): [Create opt-out recovery popups](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/). ## Before you start Developer assistance needed You build the template in the Control Panel. Share the [events guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) and [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) with your development team — they handle the two prerequisites below. Pushwoosh never shows the system permission dialog by itself. There is nothing to configure in the Control Panel to delay it. The dialog appears only when the user taps the accept button in your in-app. Work with your development team on these prerequisites: 1. Confirm the app does not request push permission earlier (including on first launch). 2. Agree on a custom event name and when to fire it, for example `onboarding_completed` right after onboarding, or `locked_feature_tapped` when the user taps a feature that needs notifications. Your team will call [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) with that name. [Learn more about events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation). ## Build the in-app Caution Use the built-in editor for this guide. A [native in-app template](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/) can’t run custom JavaScript, so the accept button won’t work if you create the template with **Native in-app**. 1. Go to **Content** → **In-Apps** and click **Create in-app** to open the [built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) directly. 2. Enter a template name. 3. Open the **Blocks** tab in the right panel and add a text or heading block with your pitch for enabling push. ![Editor canvas with a Stay in the loop heading and pitch text added, Blocks tab open in the right panel](https://docs.pushwoosh.com/show-in-app-before-push-permission-prompt-3.webp) 4. Add a button block for the accept action and another for the decline action. Both buttons work as soon as the in-app loads, with nothing else to wait for. ![Editor canvas with the pitch text and Enable notifications and Not now buttons added](https://docs.pushwoosh.com/show-in-app-before-push-permission-prompt-5.webp) Tip To set the accept button apart as the primary action, like in the screenshot above, select the decline button, turn on **Background** in its Styles panel, and set it to `transparent`. Turn on **Text color** too and pick a color that still reads clearly against the template’s background. 5. Select the accept button and set **Action Type** to **Custom Javascript**. 6. Paste only the code below into **onClick**. ```js pushwoosh.registerForPushNotifications(); pushwoosh.closeInApp(); ``` 7. Select the decline button and set **Action Type** to **Close In-App**. This action needs no code. ![Editor canvas with the Not now button selected and its settings panel with Action Type set to Close In-App](https://docs.pushwoosh.com/show-in-app-before-push-permission-prompt-6.webp) After you save the template: * **Accept button:** the system permission dialog appears. * **Decline button:** the in-app closes and no system dialog appears. ![Pre-permission in-app bottom sheet with Enable notifications and Not now buttons, shown over the app screen on iOS](https://docs.pushwoosh.com/show-in-app-before-push-permission-prompt-1.webp) A pre-permission in-app shown before the system prompt Tip For ZIP / developer upload only: if you build a raw HTML file for [ZIP upload](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/), use the full tags there instead of the editor **onClick** field: ```html ``` ### Skip the prompt for users who already opted in Add a [display condition](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/show-in-app-content-to-specific-users/#show-content-to-specific-users) so the in-app hides itself for users who already granted push, even if a later trigger shows the template again. Push consent status comes from [**Push Alerts Enabled**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags), a default Boolean tag the SDK sets from the device’s system notification permission. 1. In the **Layers** panel, click the first block, then Shift-click every other block to select the whole template together. 2. Turn on **Show this block conditionally**. The rule you set below applies to the whole selection at once. 3. Set the rule to **Push Alerts Enabled** **is false**. 4. Under **If value is unknown**, choose **Show block**. A fresh install with no tag value yet should still see the prompt. ![All blocks selected together in the editor, with Show this block conditionally on, rule set to Push Alerts Enabled is false, and If value is unknown set to Show block](https://docs.pushwoosh.com/show-in-app-before-push-permission-prompt-4.webp) Caution **Push Alerts Enabled** updates on the device’s next app open, not the moment the user flips the permission toggle in system Settings. A user who enables push from Settings can still see this block once more until they reopen the app. ## Trigger the in-app before permission is requested A device that hasn’t gone through registration yet has no push token, so you can’t reach it with a push notification. Trigger the in-app by the custom event you agreed on with development. 1. Ask your development team to call [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) with the agreed event name at the moment you want to ask. 2. Go to **Customer Journey Builder** → **Create Campaign** and start with a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) that listens for that event. 3. Add an In-app element and select the template you built. [Learn more about sending in-apps via Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/). 4. Launch the journey when you’re ready to show the prompt. Note The in-app doesn’t appear the instant the event fires. Delivery happens on the device’s next app open after that. ### Example scenario: ask for push permission right after onboarding Imagine you want to ask for push permission the moment a user finishes onboarding, instead of interrupting the tutorial itself. 1. Create the event `onboarding_completed` and confirm your development team fires it when the user finishes the onboarding flow. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) 2. Set `onboarding_completed` as the trigger for your journey, using a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). 3. Add an In-app element and select the [pre-permission template you built](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#build-the-in-app). 4. Launch the journey. Every user who completes onboarding sees the prompt once. The display condition from [Skip the prompt for users who already opted in](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#skip-the-prompt-for-users-who-already-opted-in) keeps it from firing again for users who already granted push. ### Check it worked The system prompt is one-shot, so testing both buttons takes two test devices (or two fresh installs) that haven’t granted or denied push yet, one for each button. On each device: 1. Fire the agreed event from the app. 2. Reopen the app and confirm the journey delivers your in-app. Then: 3. On the first device, tap the accept button and confirm the system permission dialog appears. 4. On the second device, tap the decline button and confirm the in-app closes with no system dialog. 5. After the next app open, confirm **Push Alerts Enabled** matches the permission state on each device. ## Build a segment of users who still have push disabled Filter on **Push Alerts Enabled** to find users who declined or never granted the permission. The tag is `false` when push isn’t allowed yet, including before the user has seen the system dialog, so a pre-permission device already falls into this segment. To separate users who were never asked from users who declined, use the [Push Permission Status](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) tag instead. 1. In the segment builder, add a filter by the **Push Alerts Enabled** tag. 2. Set the operator to **is false**. [Learn more about building segments by tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#boolean-tag-operators). Use this segment to hold back a repeat prompt from users who still don’t have push enabled, or to run a periodic win-back journey. See the [opt-out recovery segment](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/#opt-out-recovery-popup) for a worked example with the same tag. ## See also * [iOS push primer](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-primer/): the native, iOS-only alternative to this cross-platform HTML approach. * [Create opt-out recovery popups](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/): recover users who already disabled push (not the first ask), using the same Push Alerts Enabled tag. * [Create in-apps with JavaScript](https://docs.pushwoosh.com/developer/guides/messaging-channels/inapp-with-javascript/): the full JavaScript bridge reference. --- # Media storage **URL:** https://docs.pushwoosh.com/product/content/media-store/ Upload, find, or generate images in Pushwoosh and reuse them across all your content. Crop them without leaving the editor. --- The Media storage is your project’s built-in image library. Upload images, import free stock photos, or generate images with AI. Then reuse them in push notifications, web push, emails, in-app messages, Cloud Pages, and other content, directly in the editor or via a stable URL. To open the Media storage, go to **Content > Media storage** in the Pushwoosh Control Panel. The page has three tabs: * **Library:** the images already stored in the project. * **Stock photos:** search free photos from Pexels and save them to the library. * **Generate with AI:** describe an image and save the result to the library. ![Media storage page with Library, Stock photos, and Generate with AI tabs, the upload dropzone, and an image grid with Figma badges](https://docs.pushwoosh.com/content-media-store-1.webp) ## Who can add and delete images To add images (upload, stock import, AI generation), create crops, or delete images, you need the **Modify applications** permission for the project. With view-only access you can browse the library and pick images, but not change the library. See [Multi-login accounts](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/). ## Upload images To upload images to the Media storage: * **Drag and drop** one or more image files onto the upload area. * **Click the upload area** to open the file picker and select images from your device. ![Media storage upload area with an image file being dragged onto the dropzone during upload](https://docs.pushwoosh.com/content-media-store-2.webp) Note You can upload up to 5 images at once. The Media storage accepts JPG, PNG, WebP, and GIF. Other file types are ignored. **Size limits:** * JPG, PNG, and WebP up to 10 MB each. * GIF up to 10 MB. When the upload finishes, your images appear in the grid below with the most recent first, and you can click **Previous** or **Next** to browse older pages if needed. ### Compress PNG images on upload PNG compression makes PNG images smaller when they are added to the library. Pushwoosh reduces the number of colors in the image and keeps transparency. The compression is lossy, but the difference is usually not visible. It’s turned off by default and is set separately for each project. To change this setting, you need the **Modify applications** permission for the project. To turn on PNG compression: 1. On the Media storage page, click **Settings** in the upper-right corner, to the right of the page title. 2. In the **Media storage settings** window, turn on **Compress PNG images on upload**. 3. Click **Save**. With the setting on, Pushwoosh compresses every new PNG after it passes the size limits. This covers: * PNG files you upload from your device. * PNG images [sent from Figma](https://docs.pushwoosh.com/product/content/media-store/#send-images-from-figma) and [imported from Canva](https://docs.pushwoosh.com/product/content/media-store/#import-designs-from-canva). * [Crops](https://docs.pushwoosh.com/product/content/media-store/#create-image-crops) of PNG and WebP images, because those crops are saved as PNG. Pushwoosh stores JPG, WebP, and GIF files as they are. It doesn’t compress animated PNGs or images already in the library. If a PNG can’t be compressed, Pushwoosh stores the original file and the upload still succeeds. When you upload PNGs from your device, the confirmation message shows the percentage and the amount of space that compression saved, for example **Image uploaded. PNG compressed: saved 63% (81.2 KB)**. For a batch, the numbers cover only the PNGs that got smaller. The same message appears when you upload images in the [image picker of an editor](https://docs.pushwoosh.com/product/content/media-store/#select-an-image-in-the-editor). ### Send images from Figma To send frames from a Figma file without exporting them first, use the Pushwoosh plugin for Figma. See [Figma integration](https://docs.pushwoosh.com/product/integrations/figma-integration/). ### Import designs from Canva To import pages of your Canva designs, click **Import from Canva** next to the upload area. After you connect Canva, you can also open stored images in the Canva editor with **Edit in Canva** and start new designs with **Create in Canva**. See [Canva integration](https://docs.pushwoosh.com/product/integrations/canva-integration/). ## Find an image by file name Type part of a file name in the **Search by file name** field above the grid and press Enter. The search ignores letter case and covers the whole library, not only the current page. Clear the field to show all images again. ## Where an image came from A badge on the tile shows how an image got into the library: * **AI:** generated in the **Generate with AI** tab. * **Pexels:** imported from the **Stock photos** tab. * **Canva:** imported or edited with the [Canva integration](https://docs.pushwoosh.com/product/content/media-store/#import-designs-from-canva). * **Figma:** sent with the [Pushwoosh plugin for Figma](https://docs.pushwoosh.com/product/content/media-store/#send-images-from-figma). Images uploaded from your device have no badge. A badge such as **2 ver.** means the image has several versions, which appear when a Figma frame is sent again or the image is edited in Canva. The tile shows the latest version. Each version has its own URL, so content that uses an older version keeps showing that version. ### Use an older version of an image To view all versions of an image and pick one of them: 1. Click the **N ver.** badge on the tile. The **Versions** panel opens with the latest version, marked **current**, in the preview. 2. Under **All versions**, click the version you want. The list starts with the newest version. The preview shows the selected version with its size in pixels and file size. 3. Click **Use this version**. ![Versions panel with Version 1 of an Autumn sale banner selected under All versions, its size 1080 × 1080 and 452 KB, and the Use this version button](https://docs.pushwoosh.com/content-media-store-7.webp) On the Media storage page, **Use this version** copies the URL of the selected version, the same way as the [Select](https://docs.pushwoosh.com/product/content/media-store/#copy-an-image-url) button. In an editor’s image picker, it inserts the selected version into the field. ## Add a stock photo from Pexels The **Stock photos** tab searches the free photo library of [Pexels](https://www.pexels.com). Stock photos don’t count toward the [daily AI image limit](https://docs.pushwoosh.com/product/content/media-store/#daily-limit-for-ai-images). 1. Open the **Stock photos** tab. 2. Enter what you are looking for, for example `summer sale`, and click **Search**. 3. Click a photo. Pushwoosh copies the photo into your library with the **Pexels** badge. Under each search result, the photographer’s name links to their Pexels profile, and the tab credits Pexels as the photo provider. If nothing matches your query, the tab shows **Nothing found — try another query**. If your plan doesn’t include AI images, stock photo search is turned off too. See [Daily limit for AI images](https://docs.pushwoosh.com/product/content/media-store/#daily-limit-for-ai-images). ## Generate an image with AI The **Generate with AI** tab creates an image from a text description and saves it to your library. 1. Open the **Generate with AI** tab. 2. Describe the image, for example `flat-lay of summer picnic essentials on a pastel background`. 3. Choose an aspect ratio. When you open this tab from an editor, the list offers only the ratios that fit the field, so it can be shorter or missing. 4. Choose how many images to generate: **1 × image**, **2 × image**, or **4 × image**. 5. Click **Generate**. Every generated image is saved to the library with the **AI** badge, whether or not you use it. To get a new result for the same description, click **Generate again**. If the project has a [Brand book](https://docs.pushwoosh.com/product/content/brand-book/), Pushwoosh adds its colors and tone of voice to the description, so generated images follow your brand style. ### Generate an image for a push notification To generate an image that fits the message of a push: 1. In a [push preset](https://docs.pushwoosh.com/product/content/push-presets/#add-media-content) that already has a title or text, open the image picker for **Banner URL**, **Icon**, or the inbox image. 2. Open the **Generate with AI** tab. The **Match the push message** checkbox is selected by default. 3. Describe the image and click **Generate**. With the checkbox selected, Pushwoosh generates an image that illustrates the message. Pushwoosh doesn’t put the message text into the picture unless your description asks for it. In a multi-language preset, the title and text are taken from the default language, or from the first language that has them filled in. ### Use your images as a style reference A **style reference** is an image from your library that tells the AI which look you want. The generated image takes the colors, lighting, rendering technique, and mood of the references and shows the subject you describe. People, faces, logos, lettering, and scenes from the references are not copied. 1. Open the **Library** tab. 2. Select the checkbox on each image you want to use as a reference. You can use up to 10 references. 3. Click **Use as style**. The **Generate with AI** tab opens and shows the selected images under **Style references**. 4. Describe the image and click **Generate**. ![Generate with AI tab with a description entered and three autumn photos added under Style references: 3 / 10](https://docs.pushwoosh.com/content-media-store-6.webp) To remove one reference, click the cross on its thumbnail. To remove all of them, click **Clear**. You can add more images from other library pages until the total reaches 10. If the selection would go over 10, the extra images are not added. References can only be images from this project’s library. Crops can’t be used as references. ### Daily limit for AI images The number of AI images is limited per account per day. By default, the limit is: * 10 images per day on paid plans. * 2 images per day on free plans. Each generated image counts toward the limit, so **4 × image** uses four. If fewer images are left than you requested, Pushwoosh generates as many as the limit allows. When the limit is reached, generation fails with the message `daily limit of N AI images reached, try again tomorrow`. Generation with [style references](https://docs.pushwoosh.com/product/content/media-store/#use-your-images-as-a-style-reference) has its own daily limit of requests, five times the image limit. With the default image limit, that is: * 50 requests per day on paid plans. * 10 requests per day on free plans. A request counts once, however many references and images it has. Each generated image still counts toward the image limit. When the request limit is reached, generation with references fails with the message `too many generations with style references today, try again tomorrow`. Generation without references keeps working. If your plan doesn’t include AI images, generation and stock photo search fail with the message `AI images and stock photos are not available on your plan`. To change the limit, contact your Pushwoosh account manager. ## Create image crops A **crop** is a resized, cropped copy of an original image at a specific aspect ratio. Crops are useful when the same image needs different proportions for different channels or placements. To create a crop: 1. Hover over an image and click the **Create crops** button. The **Create crop** panel opens. 2. Choose an **Aspect ratio** that fits the placement where you will use the image. For example, use **2:1** for an Android big picture banner and **3:1** for an Apple Wallet strip or logo image. A frame of that aspect ratio, as large as the image allows, appears in the center of the image. 3. Move the frame over the part of the image you want to keep. Drag it with the mouse or on a touchscreen, or click a point on the image to center the frame there. To move it with the keyboard, press Tab until the frame is focused, then press the arrow keys. The frame stops at the edges of the image. 4. Set the **Size**. Choose whether your value defines the **Width** or the **Height**, then enter the number of pixels. Pushwoosh calculates the other dimension from the selected aspect ratio. 5. For JPEG images, set the **JPEG quality** value from 1 to 100. A higher value keeps more detail but produces a larger file. 6. Click **Create crop**. ![Create crop panel with 1:1 selected, width 1080, and a square frame moved to the right edge of a photo so it covers the white sneakers and only part of the gift bag](https://docs.pushwoosh.com/content-media-store-5.webp) Pushwoosh cuts the crop exactly inside the frame and saves it alongside the original, and the preview switches to the new crop. When you choose another aspect ratio, the frame returns to the center. A crop is never larger than the original. If the size you set needs more pixels than the original has at that aspect ratio, Pushwoosh saves the crop at the largest size the original allows. For example, a 1:1 crop with a width of 1080 from a 1184×864 image is saved at 864×864. You can create several crops with the same aspect ratio if their sizes or frame positions differ. If a crop with the same aspect ratio, final size (after it’s reduced to fit the original), and frame position already exists, Pushwoosh opens that crop instead of creating a new one. ### View existing crops The **Existing crops** list at the bottom of the **Create crop** panel shows every crop of the image. To view a crop: 1. Hover over the image and click the **Create crops** button. 2. Under **Existing crops**, click the crop. The preview shows the stored crop with its aspect ratio and size, for example **2:1 · 1024×512**. ![Create crop panel previewing the stored 2:1 crop of a white sneaker, 1024×512, selected under Existing crops next to a 1:1 crop](https://docs.pushwoosh.com/content-media-store-8.webp) To go back to the original, click **Original** under **Aspect ratio**. ## Delete images You can delete images you no longer need, either an original together with all of its crops, or a single crop on its own. #### Delete an original image 1. Hover over the image in the grid and click the **Delete** icon. 2. In the confirmation window, click **Delete**. If the image has several versions, only the latest version and the crops of that version are deleted, and the tile shows the previous version. To delete an older version, delete the newer versions first. You can’t delete an image used in a Wallet pass campaign. Remove the image from the campaign first. Caution Deleting an original also removes all of its crops, and this can’t be undone. Any content that references the deleted image URL will stop loading the image, so make sure the image is no longer in use before you delete it. #### Delete a single crop without removing the original 1. Hover over the image and click the **Create crops** button to open the **Create crop** panel. 2. In **Existing crops**, find the one you want to remove and click its **Delete crop** button. 3. In the confirmation window, click **Delete**. #### Delete multiple images at once To remove several original images in one action: 1. Check the checkbox on each image you want to delete, or click **Select all on page** to select every image on the current page. 2. Click **Delete selected**. 3. In the confirmation window, click **Delete**. Caution This deletes each selected original together with all of its crops, and this can’t be undone. Any content that references a deleted image URL will stop loading the image, so make sure none of the selected images are still in use. ## Use images in your content How you add a Media storage image depends on the content type. ### Select an image in the editor In the editors listed below, you can pick, find, or generate an image without leaving the editor. The image picker has the **Library**, **Stock photos**, and **Generate with AI** tabs: * **Push notifications and web push content:** **Banner URL**, **Icon**, **Stories**, and inbox images. See [Add media content](https://docs.pushwoosh.com/product/content/push-presets/#add-media-content) in Push presets. * **SMS presets:** [MMS attachments](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-mms-attachments). MMS accepts only JPG and GIF, so this picker has **Library** and **Stock photos** only. * **Push sending form:** the inbox icon and WhatsApp media. * **Customer Journey:** images in Viber and WhatsApp elements. * **Native in-app messages**, **Wallet passes**, and **email ad creatives**. * **AI Email Builder**, and the same builder for in-app messages and Cloud Pages: the image gallery. A picker hides the **Generate with AI** tab when the field doesn’t accept both PNG and JPEG, because a generated image can come in either format. It hides the **Stock photos** tab when the field doesn’t accept JPEG. In Wallet pass campaigns, an image picked for the Apple Wallet **Logo** or **Strip** slot is cropped to 3:1 automatically when it is wider than that. See [Upload campaign images](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/#upload-campaign-images). Web popups let you pick an image only from the library, without the **Stock photos** and **Generate with AI** tabs. See [Images and video](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#images-and-video) in Web popup content. ### Copy an image URL Copy a URL from the Media storage and paste it into your content editor. To copy the URL: 1. Hover over the image you want in the Media storage grid. 2. Click the **Select** button (the checkmark icon) on the image. ![Media storage grid tile with the Select checkmark button and Image URL copied to clipboard confirmation](https://docs.pushwoosh.com/content-media-store-4.webp) Tip If your browser blocks automatic copying, a window opens with the URL. Select it and copy it manually. Once the URL is copied to your clipboard, paste it into the editor you are using. Where you paste it depends on the editor: * **Image URL field:** [Drag & drop editor and Cloud Page editor — Image block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#image), or [in-app editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#image) * **HTML `src` attribute:** See [Add images](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/#add-images) in the HTML code editor #### Select an original or a crop To copy the URL of an original or a crop: 1. Hover over the image in the grid. If it has crops, its tile shows a badge with the count (for example, **2 cr.**). 2. Click the **Select** button to open the menu. Each item shows a thumbnail of the original or the crop. 3. Choose **Original** to copy the URL of the full-size image, or choose a crop listed by its aspect ratio and size to copy that crop’s URL. ![Select menu on an image tile with thumbnails next to Original, 2:1 · 1024×512, and 1:1 · 1024×1024](https://docs.pushwoosh.com/content-media-store-9.webp) --- # News feeds **URL:** https://docs.pushwoosh.com/product/content/news-feeds/ Register an RSS or Atom feed so it can populate a News block in email, a News feed push preset, or a News Feeds preset in SMS. --- Beta feature notice News feeds is in beta. We’re still refining it, so expect occasional rough edges while it matures. News feeds keeps a list of RSS or Atom feeds for your application. Once a feed is registered, Pushwoosh checks it for new articles on the schedule you set, so a [News block in email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-articles-from-a-live-feed), a [News feed push preset](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#news-feed), or a [News Feeds preset in SMS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#news-feeds) can pull the newest articles at send time instead of going stale between campaigns. To open it, go to **Content → News Feeds** for the application in the Pushwoosh Control Panel. ## Prerequisites * A public RSS 2.0 or Atom feed URL that Pushwoosh can reach without a login. ## Register a feed ![News feeds page before any feed is added, with the Add feed and How to use buttons](https://docs.pushwoosh.com/content-news-feeds-get-started.webp) 1. Click **Add feed**. 2. Enter the feed’s URL in **Feed URL**. 3. Click **Check feed**. Pushwoosh reads the URL without saving anything yet. 4. Adjust **Name** if needed. It’s filled in from the feed’s own title once the check succeeds, and is what you’ll see when picking this feed in a News block or a News Feeds preset. 5. In **Check for new articles every**, choose how often Pushwoosh looks for new articles: every 1, 15, or 30 minutes, or every hour. New feeds start at 15 minutes. 6. Click **Add feed**. Check the feed before you name it, set the interval, or add it. The check reports one of: * **Articles found:** how many articles the feed returned, with the headlines of the latest three. * **We couldn’t read this feed:** the URL is unreachable or doesn’t return valid RSS or Atom data. Open the URL in a browser to confirm it returns the feed, fix it, and click **Check feed** again. * **The URL responds but contains no articles:** the feed is reachable but empty, so it can’t be saved. Use the URL of a feed that already has articles, or publish an article and check again. Once you add the feed, Pushwoosh imports its articles right away, then checks for new articles at the interval you chose. If a registered feed later goes unreachable or stops returning articles, the content that reads it still goes out. The [News block in email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-articles-from-a-live-feed) hides its card grid for affected recipients, while the [News feed push preset](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#set-a-fallback-for-when-theres-nothing-to-send) and the [News Feeds SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#choose-a-content-source-1) fall back to whatever you set under **If the feed returns nothing**. ## Track your feeds Once at least one feed is registered, the page lists your feeds in a table. Use **Search** to find a feed by name or URL, and click the **Name** column header to sort the list. ![News feeds table with three feeds, showing Last import with article counts and Feed updates set to Every 15 min, Paused, and Every 1 hour](https://docs.pushwoosh.com/content-news-feeds-table.webp) For each feed, the table shows: * **Last import:** how many articles the latest import read and when it ran, or **Not imported yet** for a feed that hasn’t been imported. * **Feed updates:** how often Pushwoosh checks the feed, for example **Every 15 min**, or **Paused** for a deactivated feed. If the latest import failed, **Error: Couldn’t read this feed** appears under the feed’s name. Hover over it to see the reason, for example “feed returned HTTP 404.” ## Manage a feed ![Row menu of a news feed open, showing Edit, Deactivate, and Delete](https://docs.pushwoosh.com/content-news-feeds-menu.webp) Click a row’s menu (**⋮**) to manage a feed: * Click **Edit**, or click the feed’s name, to change its URL, name, or check interval. If you change the URL, click **Check feed** again before saving. * Click **Deactivate** to stop checking the feed for new articles. The feed and its already-imported articles stay. Click **Activate** to resume, which imports the feed right away and then checks it at its interval again. * Click **Delete** to remove the feed and its articles from emails that read it. This can’t be undone, so deactivate the feed instead if you only want to pause it. A feed that a [feed campaign](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) uses can’t be deleted. Delete those campaigns first. --- # Product catalog **URL:** https://docs.pushwoosh.com/product/content/product-catalog/ Connect a Google Merchant XML, YML, or JSON product feed to Pushwoosh and use your product data in Products blocks and personalized messages. --- Product catalog keeps your product data in one place in Pushwoosh: titles, prices, images, stock, and links. Connect a feed, import a CSV, or add products by hand, then use them in [Products blocks](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) across your emails instead of copying the same details into every message. When your feed syncs, prices and availability update automatically, so your campaigns stay current without manual edits. The catalog is per application. To open it, go to **Content** → **Product Catalog** for the application in the Pushwoosh Control Panel. ![Product catalog page with search, category filter, and Connect feed, Import CSV, and Add Product buttons in the top bar](https://docs.pushwoosh.com/content-product-catalog-1.webp) ## Ways to populate your catalog There are four ways to get products into the catalog. Use whichever fits your setup, or combine them: * **Install the Shopify app:** if your store runs on Shopify, installing the [Pushwoosh app](https://docs.pushwoosh.com/product/integrations/shopify/) connects your store’s product feed to the catalog automatically — no feed URL to enter. This feed is Shopify’s public `products.json`, which never includes stock counts, so every product lands with the untracked/unlimited placeholder described under [currentStock](https://docs.pushwoosh.com/product/content/product-catalog/#catalog-fields). * **Connect a feed:** point Pushwoosh at your existing Google Merchant Center, YML, or JSON product feed URL and it syncs automatically. Best if you already maintain a feed for another channel (Google Shopping, Yandex Market, another ESP, and so on). * **Import a CSV:** a one-time or repeatable bulk upload, useful if you don’t have a live feed. * **Add products manually:** create individual products directly in the Control Panel. Caution Already connected your store through the [Shopify app](https://docs.pushwoosh.com/product/integrations/shopify/)? Its feed is connected automatically. Connecting a feed here replaces it — your catalog stops syncing with the store. To bring the store’s feed back, disconnect the feed on this page first, then reconnect the Shopify app: while another feed is set, reconnecting the app leaves it alone. ### Connect a feed 1. Click **Connect feed**. 2. Enter your feed URL in **Feed URL**. 3. Click **Check feed**. Pushwoosh reads the URL without importing anything and reports what it found. 4. Once the check finds products, click **Connect & sync**. Pushwoosh maps the feed’s fields to the catalog (see below) and reports how many products were added, updated, or removed. The panel opens like this, before you enter a feed URL: ![Product feed panel opened from the Connect feed button, with an empty Feed URL field, the Check feed button, and Connect and sync disabled until a feed passes the check](https://docs.pushwoosh.com/content-product-catalog-2.webp) Note A feed can’t be connected until it passes the check — **Connect & sync** stays disabled until then. Editing the URL clears a previous check, so check the new URL again before connecting. The check reports one of: * **Count and preview:** a product count with up to three product titles and prices, for example “8 products read, 1 skipped.” * **Lower-bound count:** the check only reads a paginated feed’s first page — for example, “more than 250 products read” on Shopify’s default product feed, which caps a page at 250 items. Connecting the feed still imports every page. * **Empty feed:** the URL answers, but nothing in it parses to a product — for example, it points to a password-protected or empty store, or to the wrong export. Fix the URL or the feed’s content, then check it again. A feed like this can’t be connected, since it would import nothing. * **Read error:** the URL is unreachable, requires a login the feed can’t provide, or doesn’t return valid feed data. Once connected, the feed **syncs automatically every hour**. You can also click **Sync now** at any time to trigger an immediate sync. For example, run a sync right after you update your feed for a flash sale. If the feed hasn’t changed since the last sync, Pushwoosh skips reprocessing it and confirms nothing changed. Each sync reports: * **Imported:** new or updated products. * **Removed:** products removed from the catalog because they’re no longer in the feed. * **Skipped:** feed entries that couldn’t be mapped (missing required fields, and so on). Note **JSON**, **Google Merchant XML**, and **YML** (Yandex Market Language) formats are supported. Click **Disconnect feed** to stop syncing. Products already imported stay in the catalog — disconnecting doesn’t remove them. #### Google Merchant XML field mapping If you connect a Google Merchant feed, its standard `g:` tags map to catalog fields like this: | Feed field | Catalog field | | :----------------- | :---------------- | | `g:id` | Product ID (`id`) | | `title` | Title | | `description` | Description | | `g:image_link` | Image URL | | `link` | Product URL | | `g:price` | Price + currency | | `g:sale_price` | Sale price | | `g:product_type` | Category | | `g:mpn` / `g:gtin` | SKU | | `g:availability` | Stock | A JSON feed should be either a bare array of products or an object with a `products` array, using the same field names as the [CSV structure](https://docs.pushwoosh.com/product/content/product-catalog/#import-a-csv) below (`image_url`, `sale_price`, `stock`, and so on) — not the catalog field names shown under [Catalog fields](https://docs.pushwoosh.com/product/content/product-catalog/#catalog-fields), which use a different naming convention. Note If your feed is shaped like a Shopify storefront export (a `variants` array per product), Pushwoosh reads `inventory_quantity` from the same variant it already uses for price and availability, and uses it as the real stock count instead of the untracked/unlimited placeholder. An explicit `available: false` still forces the product out of stock regardless of quantity. Shopify’s public `products.json` endpoint never includes `inventory_quantity` (it’s only available through the Admin API), so products synced from that endpoint always get the untracked/unlimited placeholder — see the note above about installing the Shopify app. #### YML field mapping If you connect a YML (Yandex Market Language) feed, its `` fields map to catalog fields like this: | Feed field | Catalog field | | :---------------------------------------------------------------- | :---------------------------------------------------------------------------------------------------------------- | | `offer/@id` | Product ID (`id`) | | `name`, or `typePrefix` + `vendor` + `model` if `name` is missing | Title | | `description` | Description | | `url` | Product URL | | `price` | Price — when `oldprice` is missing or not greater than `price` | | `oldprice` | Price, if it’s greater than `price`; `price` then becomes Sale price, shown alongside the struck-through original | | `currencyId` | Currency | | `categoryId` | Category, resolved to its full path (`Parent > Child`) | | First `picture` | Image URL | | `vendorCode` or `barcode` | SKU | | `available="false"` | Stock (`0`). A missing `available` attribute means the offer is in stock. | ### Import a CSV 1. **Prepare your CSV.** Build a file with the columns below. CSV file structure | Column | Required | Notes | | :------------ | :------- | :--------------------------------------------------------------------------------------------------------------- | | `id` | Yes | Unique product identifier | | `title` | Yes | Product name | | `description` | No | Short product description | | `image_url` | No | Direct link to product image | | `url` | No | Link to product page | | `price` | No | Original price, decimal format (for example, `22.00`) | | `sale_price` | No | Discounted price, leave empty if none | | `currency` | No | 3-letter code: `USD`, `EUR`, and so on | | `category` | No | Product category | | `sku` | No | Stock keeping unit | | `stock` | No | Number of items in stock. Leave empty only for products that are genuinely out of stock — see the warning below. | The first row must be a header. Use UTF-8 encoding and comma-separated values. The file can’t contain columns outside this list — an unrecognized column fails the entire import, not just that column. Caution An empty `stock` cell is imported as `0`, and Products blocks pulling from a [catalog rule](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-products-with-a-catalog-rule) only show in-stock products. If your CSV omits `stock` or leaves it blank for products that are actually available, those products silently disappear from every Products block that pulls from that rule instead of showing up. Fill in a real stock count (or a high placeholder like `99999` for untracked/unlimited items) for anything you want to be eligible. 2. Click **Import CSV**, download the template if you need the exact headers, and upload your file. ![Import CSV dialog in Product catalog with template download and file upload](https://docs.pushwoosh.com/content-product-catalog-3.webp) Products are matched and updated by their **id** column, so re-uploading the same file with changed values updates existing products instead of duplicating them. ### Add products manually Add one product at a time when you don’t need a feed or CSV. 1. Click **Add Product**. 2. Fill in the product details. See [Catalog fields](https://docs.pushwoosh.com/product/content/product-catalog/#catalog-fields) for what each field means. 3. Click **Save**. ![Add product form with Product ID, SKU, Title, Description, Price, Sale price, Currency, Category, Stock, Image URL, and Product page URL fields](https://docs.pushwoosh.com/content-product-catalog-4.webp) ## Manage your catalog After you add or import products, they appear in a catalog list where you can review and manage them. ![Product catalog list with search, category filter, product rows, and an open row menu with Edit and Delete actions](https://docs.pushwoosh.com/content-product-catalog-5.webp) Use the catalog list to: * Search products by name. * Filter them by category. * Review product details such as category, price, SKU, and stock. * Open the **⋮** menu on a product row and click **Edit** or **Delete**. * Open the **⋮** menu in the top bar and click **Delete all products** to remove every product from the catalog. ### Catalog fields Every product in the catalog has these fields, regardless of how it was added: | Field | Description | | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **id** | Stable product identifier. Used to match updates from a feed or CSV, and to reference the product from a Products block. | | **title** | Product name. | | **description** | Product description. | | **imageUrl** | URL of the product image. | | **url** | Link to the product page. | | **price** | Regular price. | | **salePrice** | Discounted price, if any. Shown as a struck-through original price alongside the sale price in Products blocks. | | **currency** | Currency code for price and salePrice. | | **category** | Product category, used for filtering and for category-based rules in the Products block. | | **sku** | Stock-keeping unit / product code. | | **currentStock** | Quantity in stock. `0` means the product is treated as out of stock: it stays in the catalog but won’t appear in Products blocks that pull from a [catalog rule](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-products-with-a-catalog-rule). For an untracked or unlimited quantity, set a high value (99999 or more) — the catalog list shows it as ∞. | Caution The ∞ display only applies to the catalog list. Open **Edit** on a product with an untracked/unlimited stock and the Stock field shows the raw number (`99999`) instead — there’s no ∞ toggle in the form. Saving always writes back whatever’s in that field, so if you change it while editing something else, you’ll replace the untracked/unlimited marker with a real, much smaller count. To restore it, edit the product again and set Stock back to `99999` (or higher), then save. ### Keep your catalog up to date Use the same method you chose to populate the catalog when you need to add products or refresh existing ones: * **Feed:** Pushwoosh syncs the connected feed automatically every hour. Open **Connect feed** and click **Sync now** to refresh immediately. New products are added, changed fields are updated, and products removed from the feed are deleted from the catalog. If a sync reads zero products instead (for example, the feed goes down or its URL starts returning an empty response), Pushwoosh skips that sync instead of clearing your catalog — existing products stay, and **Last sync** shows **Sync failed** until a later sync succeeds. See [Connect a feed](https://docs.pushwoosh.com/product/content/product-catalog/#connect-a-feed). * **CSV:** Upload an updated file through **Import CSV**. Products match on **id**, so new rows add products and changed rows update existing ones without creating duplicates. See [Import a CSV](https://docs.pushwoosh.com/product/content/product-catalog/#import-a-csv). * **Manual:** Click **Add Product** to create a new item, or **Edit** on an existing row to update one product. See [Add products manually](https://docs.pushwoosh.com/product/content/product-catalog/#add-products-manually). If you hand-pick catalog products in a [Products block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products), click **Refresh from catalog** in the block settings to pull the latest title, description, price, and image into your email. ## What you can do with your catalog Once your catalog has products, use them in a [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block: pick items by hand, or pull them with a [catalog rule](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-products-with-a-catalog-rule). [How to send an email with products from your catalog](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-send-catalog-products-in-email/) walks through the full flow, from a fixed pick to an always-fresh rule. To show each shopper their abandoned cart instead, set the Products block source to [Cart](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart) and follow [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/). To show catalog products on your website, ask your developers to add [on-site recommendations](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/) with the Web SDK. First, turn on **Show recommendations on my website** under **On-site recommendations** in the catalog, as described in [Turn on on-site recommendations](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/onsite-recommendations/#turn-on-on-site-recommendations). --- # Create push presets **URL:** https://docs.pushwoosh.com/product/content/push-presets/ Create push notification presets to save, manage, and reuse message content across campaigns. Build personalized push notifications with titles, messages, and media. --- Pushwoosh empowers you to create reusable push content. You can craft the text and configure settings using our push content editor. You can then either send the push notification immediately or save it for future campaigns. Tip Watch this video to learn how to create push content in Pushwoosh, or refer to the guide below for more detailed instructions. [Youtube Video: How to create push content in Pushwoosh](https://youtube.com/watch?v=EDHBlkXB7dk) To create your push content, navigate to **Content > Push Presets** and click **Create content**, then choose a content type from the dropdown: * [**Push notification**](https://docs.pushwoosh.com/product/content/push-presets/#add-a-push-message). Write the content yourself. * [**Product recommendations**](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#product-recommendations). Build it from your product catalog. * [**News feed**](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#news-feed). Pull it from a connected news feed. * [**Push stories**](https://docs.pushwoosh.com/product/content/push-presets/build-push-stories-content/). Build a full-screen, Instagram-style stories experience for an expanded iOS notification. * [**Sponsored push**](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign). Sell the push to an advertiser from your [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/). Note The content type you pick here is fixed once you save the preset. You can’t switch a saved preset to a different content type later. For **Product recommendations** and **News feed**, Pushwoosh marks the preset with a badge next to the language selector and adds a **Content source** section for setting up the catalog or feed recipe. See [Build push content from a catalog or feed](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/) for how to configure it. You can also click **Create new content** from within a [Push element in Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#select-or-create-push-content), but that always creates a **Push notification** preset. Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, **Create new content** isn’t available from the one-time push form. Pick an existing, approved preset instead. Creating and editing presets from **Content** → **Push presets** still works as described above. ## Define the languages you want to use in your push Start by creating the default message content, which is sent to all users unless a more specific language version is provided. You can then add content for additional languages to tailor the message to users’ device language settings. For how multi-language messaging works across channels, see [Multi-language messaging](https://docs.pushwoosh.com/product/personalization/multi-language/). To add more languages: 1. At the top of the editor, click **Plus** next to the language dropdown. 2. In the **Select languages** window, select the languages you need. 3. To add a language that no device uses yet, for example when you’re planning to enter a new region, select **Include languages without devices** and then select the language. 4. Click **Apply**. If the preset already has more than one language, open the language dropdown and select **Choose languages…** instead of clicking **Plus**. The **Select languages** window lists the languages your app’s devices use, with the device count next to each, and any language already in the preset. To find a language, type its name or its language code, for example `ja` or `zh-hans`. ![Select languages window with device counts next to each language and the Include languages without devices option highlighted](https://docs.pushwoosh.com/content-presets-47.webp) Note The language selector here only counts devices on platforms that receive push notifications. Language values set through user-level Tags aren’t tied to a device platform, so they don’t show up here. *For example, if your default language is English but you want to reach Spanish and German speakers as well, add those languages to your push notification. Users with Spanish or German phone settings will then receive the message in their preferred language, while others will see the default English version.* ## Create push content Next, begin creating your push content. ### Add a push title Push titles are not required, but you can use them if you prefer to display a unique phrase instead of your app’s name in every push. To add a title, simply enter the text in the designated field. You can also add [personalization](https://docs.pushwoosh.com/product/content/push-presets/#personalize-push-content) and emojis. ![Push title input field with personalization and emoji options](https://docs.pushwoosh.com/content-presets-2.webp) ### Add a subtitle Enter the subtitle text to provide extra context, details, or make your notification more engaging. The subtitle shows on iOS and macOS only. ![Push subtitle input field for iOS devices](https://docs.pushwoosh.com/content-presets-3.webp) ### Add a push message Enter the text you want your users to see in the push notification. This is the primary content they will receive on their devices. You can also add emojis and incorporate [personalization](https://docs.pushwoosh.com/product/content/push-presets/#personalize-push-content) elements to make your message more relevant to each user. The preview helps ensure your text displays fully on different devices. To generate the text instead of typing it yourself, or to translate it into your other selected languages, use the **AI Composer** and **AI Translate** buttons below the field. See [Push AI composer](https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer/) and [Translate with AI](https://docs.pushwoosh.com/product/content/push-presets/translate-push-content-with-ai/). To bring in translations from a file instead, use the **Import** and **Export** buttons next to the language selector. See [Import and export translations](https://docs.pushwoosh.com/product/content/push-presets/import-export-translations/). ![Push message input field with preview and personalization options](https://docs.pushwoosh.com/content-presets-4.webp) ### Personalize push content Pushwoosh allows you to personalize push notification elements like titles, subtitles, and messages. This means you can tailor your content to individual users for a more engaging experience. For this: 1. Click on the **Personalize** link above the title, subtitle, or message field you want to personalize. 2. A dropdown menu will appear, displaying available user data tags. Select the tag that best suits your personalization goal (e.g., user name, city, etc), or type a name that isn’t a tag in your account yet — for example, a placeholder for a Journey attribute you plan to add later. 3. Choose the desired format modifier. The options depend on the tag’s type — for a String tag: * **CapitalizeFirst.** The first letter of the tag’s value will be written in uppercase and the rest will appear in lowercase. For example: “*john doe*” becomes “*John doe*”. * **CapitalizeAllFirst.** The first letter of each word in the tag’s value will be written in uppercase. For example: “*john doe*” becomes “*John Doe*”. * **UPPERCASE.** Converts all letters in the tag’s value to uppercase. For example: “*john doe*” becomes “*JOHN DOE*”. * **Lowercase.** Converts all letters in the tag’s value to lowercase. For example: “*JOHN DOE*” becomes “*john doe*”. * **Regular.** Keeps the text formatting as it is in the original data. For example, if a user chose to indicate their name as “*john doe*” in your app’s account settings, it will remain “*john doe*” in the personalized push copy. Integer, Price, and Date tags offer a different set of modifiers instead: * **Price tags:** regular, cent, dollar, comma, euro, jpy, lira. * **Integer tags:** the same modifiers as Price, plus 8 date formats, since an Integer tag can also hold a Unix timestamp. * **Date tags:** the same 8 date formats, plus regular. See the [Presets API reference](https://docs.pushwoosh.com/developer/api-reference/presets-api/#accepted-modifiers) for the exact date format strings. A format modifier is required for every placeholder. If you leave it unset, the field turns red and Pushwoosh blocks saving with “A personalization placeholder is incomplete: pick a modifier for it”. 4. In the **Default Tag Value** field, enter the text that should be used if the tag data is not available for a user. For instance, for a `City` tag you might enter “your travel destination” as the default value. 5. Click **Insert**. ![Personalization interface showing tag selection and format modifiers](https://docs.pushwoosh.com/content-presets-5.webp) *For example, imagine you run a travel app and want to target users exploring a new city with a push notification promoting tours and guides. To personalize the message, select the City tag and apply the **CapitalizeFirst** format modifier.* You’re not limited to tags already collected — you can personalize with a tag name your app doesn’t send yet. [Learn more about tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) Note **Example notification** **If City data is available** *Explore, dine, and discover with our app in **[Capitalized City Name]**. Start your adventure now!* **If City data is not available** *Explore, dine, and discover with our app in **your travel destination**. Start your adventure now!* ![Personalized push notification example showing city name replacement](https://docs.pushwoosh.com/content-presets-6.webp) ### Validate Liquid syntax If you use [Liquid templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) in your push title, subtitle, or message, you can check them for syntax errors before saving. This helps you catch issues like unclosed tags or typos early, so your personalization renders correctly when the push is sent. To run the check, click the **Validate Liquid Syntax** button. Pushwoosh checks the title, subtitle, and message in every language you’ve added. If it finds a syntax error, a notification appears indicating the issue. Note The check validates the Liquid syntax only. It doesn’t verify that the tags or event attributes you reference exist or that values are available for every user. ### Create dedicated copy in all languages you’ve selected If you want to send push content in multiple languages, you’ll need to create content for each language you’ve chosen. Select a language from the dropdown menu and fill in all the fields as described above. ![Multi-language push content creation interface](https://docs.pushwoosh.com/content-presets-7.webp) ### Add media content You can optionally add images to your push notifications to improve their visual appeal and potentially increase user engagement. Supported formats include JPG, JPEG, PNG, and GIF. To add media content, enable the **Media content** toggle, then fill **Banner URL** or **Icon**: * Paste a URL directly into the field. * Click the image icon next to the field to open the [Media storage](https://docs.pushwoosh.com/product/content/media-store/). Select an image you have already uploaded, find one in **Stock photos**, or create one in **Generate with AI**. If the preset already has a title or text, the **Match the push message** checkbox lets AI generate an image for this push. See [Generate an image for a push notification](https://docs.pushwoosh.com/product/content/media-store/#generate-an-image-for-a-push-notification). The image in **Banner URL** appears in the expanded notification and in the push preview. ![Media content panel with Banner URL and Icon filled in, and the resulting banner thumbnail in the push preview](https://docs.pushwoosh.com/content-presets-8.webp) You can also point **Banner URL** to a generated barcode image showing a recipient’s voucher code, but only when this preset is sent from a Customer Journey Push element with **Use Vouchers** turned on. See [Display a voucher as a barcode](https://docs.pushwoosh.com/product/content/vouchers/#display-a-voucher-as-a-barcode). ![Push preset Media content panel with the barcode URL pasted into the Banner URL field](https://docs.pushwoosh.com/content-presets-media-url-barcode.webp) Developer assistance needed When adding media content for push notifications on iOS, additional configuration is required to ensure proper media display. You’ll need assistance from your development team for this setup. Please share the [iOS Rich Notifications Integration guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-rich-notifications-integration) with them for detailed instructions. ### Add a sponsored ad campaign Choose **Sponsored push** as the content type to sell this push to an advertiser through a campaign from your [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/). ![Create content dropdown in Push Presets with the Sponsored push content type highlighted](https://docs.pushwoosh.com/content-presets-sponsored-push.webp) Note **Sponsored push** only appears in the content-type dropdown when **Ad slot** is enabled for your account and you have the **See email ads** permission — the same requirements as the [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) screen itself. #### Select a campaign Pick a campaign from the **Campaign** dropdown, listed by name and advertiser. Only campaigns with the **Push** channel turned on show up here. If none do yet, turn on **Push** for a campaign in the [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) first. **Campaign** is required. ![Ad campaign section of the Sponsored push form with a campaign selected in the Campaign dropdown](https://docs.pushwoosh.com/content-presets-sponsored-push-campaign.webp) #### Fallback content Write the title, message, and media the same way as any other push preset. That content is the fallback: when the campaign resolves at send time, its creative’s headline, text, and image replace what you wrote, and its target URL replaces the on-click action. Pushwoosh sends your written content instead when no creative resolves: * the campaign isn’t **Active** (it’s **Draft**, **Paused**, or **Finished**) * the campaign is **Active**, but its start date hasn’t come yet or its end date has passed * **Push** is turned off for the campaign in the [Ad inventory](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) * its send limit is reached * none of its creatives has both a headline and text #### Track performance Once this push has gone out, find its performance alongside the campaign’s email performance in its [report](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/#view-a-campaigns-report). **Push taps** counts a tap on the notification itself, not a click on the target URL. ### Define the next step for users who click on the notification By default, when users click on push notifications, they are directed to the main screen of your app or the home page of your website. However, you can customize the notification to redirect users to a specific page within your app, to a web page, or to another app such as YouTube. To do this, follow these steps: 1. Navigate to the **On-click Actions** section of the Push preset. 2. Choose a link type: * **Link to web page:** a web address that opens for subscribers on every platform, including app users. * **Deep link or URL:** a link for app users only, set for all platforms or for a single one. ![On-click actions section with the Link to web page and Deep link or URL buttons](https://docs.pushwoosh.com/content-presets-9.webp) #### Link to web page Enter the address in **Web Page URL**. It opens for web subscribers and for app users alike. If you also set a deep link, app users on the platforms it covers follow the deep link instead. Safari opens web links a bit differently, so Pushwoosh handles this for you automatically. When a Safari subscriber clicks your notification, it goes through a special Pushwoosh link first, which then takes them to your actual page. You’ll see this link in **URL-Address for Safari users**. It’s already filled in, and you don’t need to change anything there. Most of the time, that’s all you need to know. The second field, **URL-Address params for Safari users**, is only for pages that need extra info in the link to work correctly, like a tracking code. If your page doesn’t need that, just leave it empty. ![Web Page URL field with the read-only Safari redirect URL and an editable params field below it](https://docs.pushwoosh.com/content-presets-10.webp) #### Deep link or URL 1. Choose the target platforms: * For all platforms * Only for iOS * Only for Android * Only for Huawei ![Platform selection for deep linking (iOS, Android, Huawei)](https://docs.pushwoosh.com/content-presets-41.webp) 2. Enter the deep link URL. There are 2 ways to do it: * **Enter link manually.** Input the deep link URL for the page or feature you want users to access within your app, for example `myapp://promotion?id=42`. A regular `https://` address works here too, if you want app users on the chosen platform to leave the app for a website or another app. * **Use link preset.** If you have predefined deep link presets, you can select one from the available list. Presets can be managed in **Settings > Deep Links** if needed. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) Once a web page or deep link URL is set here, you can auto-fill the push title, message, and banner image from that page instead of typing them yourself. See [Fill content from a link](https://docs.pushwoosh.com/product/content/push-presets/fill-content-from-link/). ![On-click actions panel with the Deep link or URL form open for iOS, showing the manual link entry option](https://docs.pushwoosh.com/content-presets-42.webp) #### Open a YouTube video or another app To send app users to a YouTube video, a YouTube channel, or any other external page, you don’t need a deep link or any changes in your app. Use a regular web address: 1. Copy the address of the video or channel, for example `https://www.youtube.com/watch?v=VIDEO_ID` or `https://www.youtube.com/@channel`. 2. Add the address in one of two ways: * To open it for every subscriber, click **Link to web page** and paste the address into **Web Page URL**. * To open it for app users only, or on a single platform, follow the steps in [Deep link or URL](https://docs.pushwoosh.com/product/content/push-presets/#deep-link-or-url) and enter the address with **Enter link manually**. When a user taps the notification, the device opens the address the same way as a link tapped anywhere else. It opens in the YouTube app if the app is installed, and in the browser otherwise. This works the same way on iOS and Android. A deep link with your own scheme, such as `myapp://live-game`, is a different case. It opens a screen inside your app, so your developers have to set it up in the app first. See [Deep links](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking). ### Add buttons to the notification Add up to 3 buttons under a mobile push, each with its own action, for example **Track order** and **Cancel order**. Buttons show on iOS, Android, and Huawei. Web push doesn’t show them. Developer assistance needed Buttons need iOS SDK 7.2.10 or later and Android SDK 6.17.0 or later. On iOS, your app also needs a [Notification Service Extension](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/setting-up-pushwoosh-ios-sdk-7-0/basic-integration-guide/#add-notification-service-extension). With an older SDK, taps on buttons aren’t counted, and **Custom action** doesn’t work on Android. To add a button: 1. In the **Buttons** section, click **Add button**. 2. In **Label**, enter the button text, for example `Cancel order`. Enter it for each language of the preset. Only the default language is required. 3. In **Action**, choose what happens when the user taps the button: * **Deep link in app**: opens a screen in your app. Enter the link in **Deep link: All platforms**. To use a different link on one platform, click **Add deep link** and choose the platform. * **Link to web page**: opens the address you enter in **URL**. * **Custom action**: passes the **Button ID** you enter to your app, and your app decides what to do. The ID must match the one your app handles. 4. Optional: in **Style**, choose **Destructive** to show the button in red on iOS, for example for a cancel action. Android and Huawei ignore the style. ![Buttons section of a push preset with a Track order button linked to a web page and a destructive Cancel order button with a custom action, shown in the iOS preview](https://docs.pushwoosh.com/content-presets-buttons.webp) A label or a button ID can be up to 64 characters long. Two buttons can’t have the same ID. The **Buttons** section isn’t available for push stories. A tap on a button counts as an open of the push. Message statistics show taps on each button separately. See [Push notification statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#what-each-step-includes). ### Add Custom Data **Custom Data** allows you to include additional information within your push notifications. This information is structured as a JSON object, containing key-value pairs. By using Custom Data, you can dynamically update your app’s content, direct users to specific sections within the app, and manage actions after push notification receipt and processing. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data) To include Custom Data in your push notification, simply toggle **Custom Data** on and insert your JSON data into the Custom data field. ![Custom data modal with a JSON editor showing key-value pairs for a push notification](https://docs.pushwoosh.com/content-presets-11.webp) ## Configure notification settings Notification settings in Push presets allow you to customize how users receive and interact with notifications. You can define badge count, sound preferences, and delivery priority to align notifications with your app’s requirements. To configure these settings, click **Notification settings** under the push preview. ![Configure notification settings](https://docs.pushwoosh.com/content-presets-37.webp) ### Set badge count The badge count is the number displayed on an app icon, typically indicating unread messages or notifications. Using badge count in Push Presets ensures that notifications consistently update the app icon badge, helping users keep track of unread messages, tasks, or alerts. ![Set badge count](https://docs.pushwoosh.com/content-presets-38.webp) **Increase (+1):** Adds 1 to the current badge count. *Example:* A push notification for a new message increases the badge count from 2 to 3, indicating 3 unread messages. **Decrease (-1):** Subtracts 1 from the current badge count.\ *Example:* A push notification confirms a completed task, reducing the badge count from 5 to 4, showing 4 pending tasks. **Set a specific number:** Assigns an exact badge count. *Example:* A push notification from a news app sets the badge count to 10 when 10 unread articles are available. ### Platform-specific settings You can configure notification settings separately for **iOS**, **Android**, and **Huawei** platforms by switching between the respective tabs. #### iOS notification settings ![iOS notification settings](https://docs.pushwoosh.com/content-presets-39.webp) ##### Sound Select a notification sound from the dropdown menu. * **Default**: Plays the system’s default notification sound. * **Mute**: Disables sound for this notification. * **bubble.wav**. ##### Delivery priority Delivery priority determines how push notifications are handled on the user’s device, ensuring important messages are delivered appropriately based on urgency. You can select from the following priority levels: * **Critical**: Delivered immediately, even when Do Not Disturb is enabled. These notifications light up the screen, bypass the mute switch, and play a sound. Requires Apple-approved entitlement and should be used for emergency alerts (e.g., severe weather, safety warnings). * **Active**: Standard push notifications that appear instantly, light up the screen, and play sounds or vibrations. Blocked by Focus mode, meaning users won’t receive them if their device settings restrict notifications. Best for chat messages or important updates. * **Time-sensitive**: Delivered immediately even if Focus mode is enabled, with a yellow “Time-Sensitive” banner. Users can disable this interruption level. Ideal for urgent alerts like account security warnings or package delivery updates. * **Passive**: Added to the notification center without lighting up the screen or playing a sound. These notifications do not bypass Focus mode and are best for non-urgent updates, recommendations, or promotional offers. ##### Root params Root params are raw key-value pairs merged directly into the iOS payload, for advanced settings the preset UI doesn’t expose. Add them as JSON, for example `{"key": "value"}`. #### Android notification settings ![Android notification settings](https://docs.pushwoosh.com/content-presets-40.webp) ##### Sound settings Choose how notifications play sounds when received: * **Default**: Uses the system’s default notification sound. * **Mute**: Delivers the notification silently without sound. ##### Delivery priority Delivery priority determines how push notifications are handled on the user’s device, ensuring important messages are delivered appropriately based on urgency. You can select from the following priority levels: * **High**: Ensures the notification is delivered immediately with sound and visual alerts. Suitable for time-sensitive messages like chat notifications or alerts. * **Normal**: Delivers notifications efficiently without overriding system settings. Standard priority for most notifications. ##### User-visible importance level The user-visible importance level controls how notifications appear to users, including sound, visuals, and interruptions. Choose the appropriate level to match the urgency of your notifications: * **None**: Delivered silently with no sound or visual alerts. * **Low**: No sound; does not appear in the status bar. * **Medium**: No sound; appears in the status bar. * **High**: Plays a sound and appears in the status bar. * **Urgent**: Plays a sound and appears as a heads-up notification. ##### Root params Root params are raw key-value pairs merged directly into the Android payload, for advanced settings the preset UI doesn’t expose. Add them as JSON, for example `{"key": "value"}`. #### Huawei notification settings ![Huawei notification settings](https://docs.pushwoosh.com/content-presets-46.webp) ##### Sound settings Choose how notifications play sounds when received: * **Default**: Uses the system’s default notification sound. * **Mute**: Delivers the notification silently without sound. ##### Delivery priority Delivery priority determines how push notifications are handled on the user’s device, ensuring important messages are delivered appropriately based on urgency. You can select from the following priority levels: * **High**: Ensures the notification is delivered immediately with sound and visual alerts. Suitable for time-sensitive messages like chat notifications or alerts. * **Normal**: Delivers notifications efficiently without overriding system settings. Standard priority for most notifications. ##### User-visible importance level The user-visible importance level controls how notifications appear to users, including sound, visuals, and interruptions. Choose the appropriate level to match the urgency of your notifications: * **None**: Delivered silently with no sound or visual alerts. * **Low**: No sound; does not appear in the status bar. * **Medium**: No sound; appears in the status bar. * **High**: Plays a sound and appears in the status bar. * **Urgent**: Plays a sound and appears as a heads-up notification. ##### Root params Root params are raw key-value pairs merged directly into the Huawei payload, for advanced settings the preset UI doesn’t expose. Add them as JSON, for example `{"key": "value"}`. ## Preview and save push content The push content editor provides a convenient way to visualize how your notification will appear on recipient devices. Simply choose the target platform you’d like to preview the notification for (e.g., Android, iOS). [](https://docs.pushwoosh.com/content-presets-12.webm) To ensure that your push notifications are configured correctly and display as expected, send a test push to a dedicated device using the **Test push notification** feature. [Learn more about test devices](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/) ![Push preview on the lock screen with the Test push notification button highlighted](https://docs.pushwoosh.com/content-presets-13.webp) Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, **Test push notification** is hidden entirely, regardless of the preset’s version status. Once you’ve finalized your push notification content, be sure to click the **Save** button located at the top of the editor. ![Push content editor header with the Save button highlighted](https://docs.pushwoosh.com/content-presets-14.webp) Upon saving, your push notification will be added to the [list of available push content](https://docs.pushwoosh.com/product/content/push-presets/manage-push-presets/). You can then easily access and utilize it within your future campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications) ### Save a message to the inbox Saving a push to the user’s inbox is not configured in the push preset editor. Turn it on when you send the message. See [Save a push to a user’s inbox](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/#save-a-push-to-a-users-inbox). ## Versions and submitting content for approval Pushwoosh saves a new version of this preset every time you save changes, whether or not [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account. Open the version history to compare two versions, or restore an earlier one’s text into the editor. If content approval is on, the editor also shows a version status chip next to **Save**: **Draft**, **On approval**, **Approved**, or **Rejected**. ![Push content editor showing the Draft version chip next to Submit for approval and Version history buttons](https://docs.pushwoosh.com/approvals-and-content-versions-1.webp) Click **Submit for approval** next to **Save** to send the current version for review. The chip changes to **On approval**, and a reviewer with the **Approve messages** permission sees it in their queue. Note A reviewer can’t approve or reject their own submission. It has to be a different user with the **Approve messages** permission. If the reviewer rejects it, the chip changes to **Rejected** and shows the reviewer’s reason on its second line. Edit the content and click **Submit for approval** again to resubmit. Editing a preset that’s currently **Approved** moves it back to **Draft**, with a version number next to the status, for example **Draft · v4**. Editing also cancels any other request still waiting for approval on this preset. See [Approvals and content versions](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) for the full workflow, including what happens when you try to send unapproved content. ## Understanding push content behavior in cloned Journey points When you clone a push journey element, its content remains shared between the original and the copy. Any changes made to the content in one point will automatically update the other. If you need unique content for the new point, create new push content instead of modifying the existing one. --- # Build push content from a catalog or feed **URL:** https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/ Configure a push preset that builds its title, message, and banner from your product catalog or a connected news feed, row by row. --- Build a push once, then let Pushwoosh fill it with live products or articles at send time. * Use **Product recommendations** to promote items from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog), such as new arrivals or bestsellers. * Use **News feed** to share the latest or trending articles from a registered feed. ## Prerequisites * **Product recommendations:** a [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog) with products to pull from. * **News feed:** at least one feed registered under **Content > News Feeds**. The preset editor only picks a feed. It doesn’t register one. ## How to create the preset 1. Open **Content > Push Presets**. 2. Click **Create content**. 3. Pick **Product recommendations** or **News feed**. See [Create push presets](https://docs.pushwoosh.com/product/content/push-presets/). Note Creating content from a [Push element in Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#select-or-create-push-content) always creates a **Push notification** preset. ### Compose the message 1. Enter the push title, for example `New picks for you`. It is sent once. 2. Optionally enter a subtitle, for example `Fresh from the catalog`. It shows on iOS and macOS only. 3. Enter the push message as the intro before the rows, for example `Check out what just came back in stock:`. 4. In **Content source** further down, fill **Item template** (once per row) and **Outro** (once after the rows). See [Fill in the item template and outro](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#fill-in-the-item-template-and-outro). ![Push preset Title, Subtitle, and Push message fields filled in above the AI Composer toolbar](https://docs.pushwoosh.com/build-push-content-from-a-feed-1.webp) Note To put a catalog or feed value in **Title**, **Subtitle**, or **Push message**, type a Liquid placeholder yourself, for example `{{product.title}}`. Prefer the insert buttons under **Item template** and **Outro**. **Personalization** above the message fields inserts user tags (like City), not catalog or feed values. The intro, each item-template row, and the outro are joined with a line break. **Separator between items** only applies between the repeated rows. See [Save the preset](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#save-the-preset) for what happens if a placeholder isn’t valid. ### Configure the content source In **Content source**, set the rule for the type you picked when creating the preset. #### Product recommendations 1. Set **Items** to how many products to pull, from 1 to 3. It defaults to 3. 2. Set **Category** to filter which products the rule can pick from, or leave it as **Any category**. 3. Set **Strategy** to how products are picked. 4. Set **Sort by** (**Newest**, **Price, lowest first**, **Price, highest first**, or **Title**) to order the rows the rule returns. ![Product recommendations Content source with Items 3, Strategy Back in stock, and Sort by Newest](https://docs.pushwoosh.com/build-push-content-from-a-feed-2.webp) ##### Catalog data vs. events from your backend Some strategies work out of the box. Pushwoosh reads your product catalog and ranks automatically. Others need purchase and browsing data from your backend before they can rank anything. **Work out of the box: catalog data only** | Strategy | How it ranks | | :---------------- | :-------------------------------------------------------- | | **Newest** | By date added to the catalog, newest first | | **Back in stock** | By when the product came back in stock, most recent first | | **Price drop** | By how recently the price dropped | No setup needed beyond keeping your catalog in sync with Pushwoosh. **Require product events** These strategies rank on real user behavior: purchases, browsing history, co-purchase patterns. They won’t work until Pushwoosh receives the right events from your app, website, or backend. See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/) for what to send. | Strategy | How it ranks | What it needs | | :----------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Bestsellers, 7 days** | Products with the most units sold in the last 7 days | `PW_OrderCreated` and `PW_OrderUpdated` events, each with an `items` array listing what was purchased | | **Bestsellers, 30 days** | Same, over a 30-day window | Same as above | | **Bought together** | Products other users viewed, added to cart, or bought alongside a specific anchor product in the last 30 days | Product activity events, plus a device tag holding the anchor product ID, set in the **Anchor tag** field. Without a tag, Pushwoosh skips this strategy and falls back to **Category** and **Sort by**. | | **Recently viewed** | Products that other users interacted with alongside the last 10 products this recipient interacted with, based on the last 30 days. Products the recipient already touched are left out. | Any event sent through the SDK or API with a `userId` and a product ID in `product_id`, `productId`, `item_id`, or `sku`, or in a `products` array. The ID must match the product `id` in your catalog. Without such events for the recipient, Pushwoosh skips this strategy and falls back to **Category** and **Sort by**. | Bought together and Recently viewed can't be previewed These two strategies are picked per recipient, so the editor has nothing to show for them. See [Preview the push](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#preview-the-push). #### News feed 1. Set **Articles** to how many articles to pull, from 1 to 3. It defaults to 3. 2. Set **Rubric** to filter which articles the rule can pick from, or leave it as **Any rubric**. 3. Set **Ranking**: * **Newest:** the latest articles by publish date. * **Trending:** the articles with the most recent opens on your site and clicks in your emails. 4. Set **Feed** to one of your account’s registered feeds, or **All feeds** to pull from every feed. This panel only picks a feed. It doesn’t register one; if none exist yet, register one on the [**News feeds**](https://docs.pushwoosh.com/product/content/news-feeds/) page first. ![Content source panel for a News feed preset with Articles set to 3, Ranking set to Trending, and Feed set to All feeds](https://docs.pushwoosh.com/build-push-content-from-a-feed-3.webp) How Trending works Pushwoosh counts two signals: [`PW_SiteOpened`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_siteopened), which needs the Pushwoosh Web SDK on your site, and [`PW_EmailLinkClicked`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emaillinkclicked), which fires automatically when you send emails through Pushwoosh. Without the Web SDK, **Trending** ranks on email clicks alone, a weaker signal. When there isn’t enough recent activity, **Trending** falls back to **Newest**. ### Fill in the item template and outro 1. Fill in **Item template**. For Product recommendations, click **Title**, **Price**, **Category**, or **Link** to insert a Liquid placeholder like `{{product.title}}` at the cursor. For News feed, click **Title**, **Summary**, **Category**, **Author**, or **Link** to insert the matching article field. This block repeats once per row. 2. Choose **Separator between items**: **Comma** puts rows on one line, **New line** puts each row on its own line. 3. Fill in **Outro**. The same insert buttons are available here. This block is sent once, after the rows. 4. Optionally, set **Trim product title to** (or **Trim article title to**) a character count. Leave it at 0 for no trimming. ![Item template with title and price placeholders, Outro text, and the Separator and Trim product title to fields below it](https://docs.pushwoosh.com/build-push-content-from-a-feed-4.webp) Note There’s no insert button for a picture. Set the picture as the push banner in the next section. ### Show the leading item’s picture as the banner 1. Turn on **Use the leading item’s picture as the banner** so the push uses the picture of the first product or article the feed returns. 2. Leave **Banner URL** alone. You don’t need to open **Media content** or paste a URL. Pushwoosh sets the banner link when you save. ![Toggle labeled Use the leading item's picture as the banner switched on in the push preset editor](https://docs.pushwoosh.com/build-push-content-from-a-feed-5.webp) Once it’s on, **Banner URL** under **Media content** shows the toggle’s own name instead of an editable field. ![Media content with Banner URL showing Use the leading item's picture as the banner instead of a URL](https://docs.pushwoosh.com/build-push-content-from-a-feed-6.webp) ### Set a fallback for when there’s nothing to send Under **If the feed returns nothing**, choose what happens when the rule finds no rows: * **Do not send to this recipient:** Pushwoosh skips the send to that recipient instead of delivering an empty push. * **Send this text instead:** send the fallback text you type in the field that appears. ![If the feed returns nothing options with Do not send to this recipient selected as the choice](https://docs.pushwoosh.com/build-push-content-from-a-feed-8.webp) ### Point the on-click action at the leading item Under **On-click actions**, use **Open the leading item’s link**: * **Leave it on:** the push opens the link of the first product or article the feed returns. * **Turn it off:** set a [manual action](https://docs.pushwoosh.com/product/content/push-presets/#define-the-next-step-for-users-who-click-on-the-notification) yourself (deep link or web page). ![On-click actions panel with Open the leading item's link enabled and the link shortening caution below it](https://docs.pushwoosh.com/build-push-content-from-a-feed-7.webp) Caution Turning this toggle on or off clears any manual action you had set. There is no way to restore it. If you turn the toggle off, set up the deep link or web page again from scratch. Link shortening stays off while the toggle is on, because the link is only known once the push renders. ### Preview the push Check the preview. It fills the item rows with real products or articles, fetched the same way a send would fetch them — it doesn’t show the intro or outro text. ![Push preview panel showing a News feed push rendered with real article rows on a locked iOS screen](https://docs.pushwoosh.com/build-push-content-from-a-feed-9.webp) If the preview is empty, read the note in the panel: * For **Bought together** or **Recently viewed**, this is expected. Pushwoosh picks products per recipient, so there is nothing to preview in the editor. * If Pushwoosh can’t reach the catalog or the feed, fix that connection before you send. * If the rule returned no products or articles, check [If the feed returns nothing](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/#set-a-fallback-for-when-theres-nothing-to-send). The preview may still say nothing is sent even when you chose **Send this text instead**. In that case the fallback text still goes out. Only **Do not send to this recipient** skips the send. ### Save the preset Click **Save**. Pushwoosh checks that every placeholder in the title, subtitle, and message is valid. If one isn’t, saving is blocked, and Pushwoosh points to the field and language that needs fixing. After you save, open the preset from the [list of available push content](https://docs.pushwoosh.com/product/content/push-presets/manage-push-presets/). --- # Build push stories content **URL:** https://docs.pushwoosh.com/product/content/push-presets/build-push-stories-content/ Create a push preset whose expanded iOS notification plays as a full-screen, Instagram-style stories experience with buttons. --- Build a push preset whose expanded iOS notification plays as a full-screen, Instagram-style stories experience: full-bleed images, auto-advancing pages, and a button on each page. ## Prerequisites Before you send push stories, your iOS team needs to: 1. Add a Notification Content Extension to the app using the `PushwooshNotificationUI` module. Share the [iOS push stories developer guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-stories/) with them. 2. Release an app update that includes the extension. See [iOS push stories](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/) for what recipients see without that extension. ## Create the preset 1. Open **Content** → **Push Presets**. 2. Click **Create content**. 3. Choose **Push stories** from the dropdown. ![Create content dropdown in Push Presets showing its five content type options, including Push stories](https://docs.pushwoosh.com/ios-push-stories-content-menu.webp) ### Compose the message Write the push title, subtitle, and message the same way as for a regular [push preset](https://docs.pushwoosh.com/product/content/push-presets/). ### Build the stories pages In **Stories**, fill in the fields for each page: | Field | Description | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- | | Image | URL of the page’s full-screen image. Click the paperclip to pick one from [Media storage](https://docs.pushwoosh.com/product/content/media-store/) instead of pasting a URL. | | Duration, seconds | How long the page stays on screen before auto-advancing, from 1 to 30 seconds. Defaults to 5 seconds. | | Title | Large text shown over the bottom of the page’s image. | | Subtitle | Smaller text under the title. | | Button title | Label of the page’s call-to-action button. | | Button link | URL or app deep link opened when the button is tapped. | Click **Add page** to add more pages, up to 10 in total. Each page plays in order and carries its own image, timing, and button. ![Page 1 of a Stories block expanded, showing its image, duration, title, subtitle, button title and link fields](https://docs.pushwoosh.com/ios-push-stories-fields.webp) ### Check the preview Use the preview panel on the right to check each page before sending. It has two tabs: * **Push:** the collapsed notification. * **Story:** the page you’re editing, including its image and button. With several pages, the preview plays them in turn, and the progress bar at the top shows one segment per page. ![Story tab of the preview auto-advancing through three pages with a three-part progress bar](https://docs.pushwoosh.com/ios-push-stories-editor-preview.webp) Note The preview only shows how the pages will look. Stories play on a device only on iOS, and only in an app version that includes the extension. ## Send push stories To send the preset, select it as the content of a one-time push. The audience is then limited to iOS. See [Send a one-time push with push stories](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/#send-a-one-time-push-with-push-stories). --- # Fill content from a link **URL:** https://docs.pushwoosh.com/product/content/push-presets/fill-content-from-link/ Auto-fill a push notification's title, message, and banner image from the Open Graph tags of the page its click action opens. --- **Fill from link** saves you from retyping a page’s headline, summary, and image into your push. It reads them straight from that page’s Open Graph tags, the same metadata social networks use to build link previews. Note This works only when the on-click action’s link (the [web page URL](https://docs.pushwoosh.com/product/content/push-presets/#link-to-web-page) or a [deep link](https://docs.pushwoosh.com/product/content/push-presets/#deep-link-or-url)) is a real URL, not one personalized with a Liquid tag or template. If more than one link is configured, Pushwoosh reads the web page URL first, then the deep link set for all platforms, then a platform-specific one. ## How to fill content from a link Fill from link needs the push’s on-click action set first, since that’s where the source page comes from. 1. In the [Push presets editor](https://docs.pushwoosh.com/product/content/push-presets/), [set up the on-click action](https://docs.pushwoosh.com/product/content/push-presets/#define-the-next-step-for-users-who-click-on-the-notification) with the link you want to pull content from. 2. With the default language selected in the language selector, leave the push title and push message empty. About half a second after you add the link, Pushwoosh fills both fields, plus the banner image, from the page. If the page has no Open Graph tags, nothing happens and your fields stay empty. Pushwoosh doesn’t show an error for this silent fill. 3. To fill another language, or to pull fresh content into fields that already have text, click **Fill from link**, next to **AI composer**, below the push message field. Note **Fill from link** fills content for one language at a time. Pick another language in the language selector and click it again to fill that language too. Clicking the button on a page with no Open Graph tags shows an error and leaves your fields as they were. --- # Import and export translations **URL:** https://docs.pushwoosh.com/product/content/push-presets/import-export-translations/ Upload your own translations for a push preset from a CSV or JSON file, or export the current content to translate elsewhere. --- Instead of generating translations with [AI Translate](https://docs.pushwoosh.com/product/content/push-presets/translate-push-content-with-ai/), bring your own from a translation vendor, a spreadsheet, or another Pushwoosh preset. Open **Content** → **Push presets**, open the preset, then use **Import** or **Export** next to the language selector in the [Push presets editor](https://docs.pushwoosh.com/product/content/push-presets/). ![Import and Export icon buttons next to the language selector in the Push presets editor](https://docs.pushwoosh.com/import-export-translations-1.webp) * **Export:** download the current title, subtitle, and message for every language as a CSV to translate elsewhere or reuse in another preset. * **Import:** upload a CSV or JSON file to apply translations to this preset. Note Import and Export cover only title, subtitle, and message text. They do not move images, buttons, links, or other preset settings. ## Export the current content Click **Export** to download the preset’s title, subtitle, and message for every language it currently has, including the default one, as a CSV file. The download name looks like `welcome-series-translations.csv` (the preset name, slugified, then `-translations.csv`). The file looks like this: | language | title | subtitle | message | | -------- | -------------------------- | -------- | ------------------------ | | default | Welcome to our app! | | Thanks for installing. | | es | ¡Bienvenido a nuestra app! | | Gracias por instalar. | | de | Willkommen in unserer App! | | Danke fürs Installieren. | ## Import translations into the preset ### Prepare the file Use the CSV from **Export** after you edit it, or build a file that matches the same layout. Import also accepts `.json`. * **language:** required header column. Write each code the same way it appears in the [language selector](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push), for example `zh-Hant` for Traditional Chinese. Letter case does not matter (`ES` and `es` both work). If the import preview lists a code Pushwoosh does not recognize, Pushwoosh still adds that language, but its text only reaches devices set to exactly that code. Fix the code in the file and import again. * **title**, **subtitle**, and **message:** optional. Columns can be in any order. A column you leave out is treated as blank for every row, the same as an empty cell in a column you kept. Each following row is one language. Note `subtitle` shows on iOS and macOS only, the same as when you [add a subtitle](https://docs.pushwoosh.com/product/content/push-presets/#add-a-subtitle) in the editor. #### Use a JSON file instead of CSV If your vendor or team already sends a `.json` file, Import accepts it too. You do not need JSON if you work with the CSV from **Export**. * **Same fields as the CSV:** `title`, `subtitle`, and `message`, grouped by language code: ```json { "default": { "title": "Welcome to our app!", "message": "Thanks for installing." }, "es": { "title": "¡Bienvenido a nuestra app!", "message": "Gracias por instalar." } } ``` * The same shape the [Presets API](https://docs.pushwoosh.com/developer/api-reference/presets-api/) uses for `localizedTitle`, `localizedSubtitle`, and `localizedContent` (these match the CSV columns `title`, `subtitle`, and `message`). Useful if you already export presets through the API: ```json { "localizedTitle": { "default": "Welcome to our app!", "es": "¡Bienvenido a nuestra app!" }, "localizedContent": { "default": "Thanks for installing.", "es": "Gracias por instalar." } } ``` Note To update many presets at once from scripts, ask your development team to use the Presets API [`UpdatePartial`](https://docs.pushwoosh.com/developer/api-reference/presets-api/#updatepartial) call. For one preset in the Control Panel, use **Import** on this page. It takes the same `localizedTitle`, `localizedSubtitle`, and `localizedContent` maps. ### Import the file 1. Click **Import** next to the language selector. 2. Choose a `.csv` or `.json` file. The preview shows how many languages will be applied, how many of those are new to this preset, and how many are skipped. 3. Check **Overwrite languages that already have text** only if you want the file to replace existing translations. Leave it unchecked to fill empty languages and add new ones without changing text you already wrote. ![Import translations modal with a CSV file loaded, the overwrite checkbox, and the languages summary](https://docs.pushwoosh.com/import-export-translations-2.webp) 4. Click **Import**. After import, switch the language selector to a language from the file and confirm the title and message look right. Note A language is skipped when nothing in its row would change anything: * every field in the row is blank, or * the preset already has text in every field that row fills in, and **Overwrite languages that already have text** is off. A language in the file that isn’t in the preset yet is added automatically. An empty cell or field never clears existing text, even with **Overwrite** checked. It’s only skipped, not written as blank. ### If Pushwoosh can’t read the file Instead of a preview, you see one of these messages: | Message | When it happens | | -------------------------------------- | ------------------------------------------------------------------------------------------------------- | | The file is empty. | The file has no content. | | The file is not valid JSON. | A `.json` file (or a file starting with `{`) can’t be parsed as JSON, or its top level isn’t an object. | | The CSV file has no “language” column. | The header row of a CSV file is missing the `language` column. | | The file has no translations. | Every row, or every JSON entry, is missing a language code. | --- # View and manage push presets **URL:** https://docs.pushwoosh.com/product/content/push-presets/manage-push-presets/ View, edit, and organize your saved push notification presets to streamline message creation across campaigns. --- ## Push Presets section overview The **Push Presets** section in your Pushwoosh Control Panel displays a list of all existing presets. It allows you to create and manage reusable push message templates. ![Push Presets section](https://docs.pushwoosh.com/content-manage-push-presets-1.webp) Here, you can: * Search presets by name or code. * Sort presets by last modified date or name. * Create a new preset by clicking the **Create content** button and choosing **Push notification**, **Product recommendations**, **News feed**, [**Push stories**](https://docs.pushwoosh.com/product/content/push-presets/build-push-stories-content/), or [**Sponsored push**](https://docs.pushwoosh.com/product/content/push-presets/#add-a-sponsored-ad-campaign) from the dropdown. **Sponsored push** appears only when **Ad slot** is enabled for your account and you have the **See email ads** permission. This choice can’t be changed once the preset is saved. [Learn more about creating push content ](https://docs.pushwoosh.com/product/content/push-presets/) ## Understanding preset details Each preset card in the list displays the following details: * Preset name (e.g., *20% OFF*, *Flash Sale*, *Promo*) * Preset code – a unique identifier used to reference the preset in API requests. * The date the preset was last modified. * A preview of the message content, including the title and body text. To view preset details, expand the preset card. The expanded view includes the following information: * **Platforms**: Lists all supported platforms for the preset, such as iOS, Android, and Huawei. * **Custom data**: Indicates whether custom data is included. * **On click action**: Defines the action triggered when the notification is clicked, specified per platform (e.g., follow a deep link, open a web page). * **Root-params section**: Contains optional parameters included with the notification, typically used for campaign tracking and deep linking. ![Preset details](https://docs.pushwoosh.com/content-manage-push-presets-3.webp) To send a one-time message directly from the list, click *Send a one-time message* and configure the push notification settings. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#how-to-send-a-push-notification-using-the-one-time-push-form) ## Edit and manage presets To manage a preset: 1. Hover over the preset you want to manage. 2. Click the three-dot menu (⋮) next to the preset name. ![Edit and manage presets](https://docs.pushwoosh.com/content-manage-push-presets-2.webp) 3. From the menu, you can: * [**Edit content**](https://docs.pushwoosh.com/product/content/push-presets/) * [**Send a one-time message**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#how-to-send-a-push-notification-using-the-one-time-push-form) * **Clone the preset** * **Delete the preset** To delete several presets at once: 1. Click **Bulk actions** → **Delete…**. 2. Select the presets. 3. Click the **Delete** button above the list. Presets used in journeys or geozones are skipped. Deleting a single preset in use fails too, and the error lists where the preset is used. Deleted presets can’t be restored. ## Review pending approvals If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, submitted presets appear on the **Pending approval** tab. A badge on the **Content** → **Push Presets** menu item shows how many versions are waiting for review. ![Pending approval tab with a submitted version and Compare, Reject, and Approve buttons, and a badge on the Push Presets menu item](https://docs.pushwoosh.com/approvals-and-content-versions-3.webp) To review a pending version, a user with the **Approve messages** permission can: * Click **Compare** to open a side-by-side diff against the previous version, whatever its own status was. * Click **Approve** to make the version sendable. * Click **Reject** and enter a reason. The reason is required, and appears on the preset’s card here as well as on its status chip in the editor. Note A reviewer can’t approve or reject a version they submitted themselves. It has to be a different user with the **Approve messages** permission. --- # Push AI composer **URL:** https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer/ Use AI to create engaging push notification content with the right tone, keywords, and audience targeting for better campaign performance. --- The **Pushwoosh AI composer** is an intelligent content generation tool that helps you create engaging push notification messages. With AI composer, you can: * Generate compelling push notification content quickly * Create variations for A/B testing * Maintain consistent brand voice across campaigns * Localize content for multiple languages ## Generate push content with AI composer To generate a message using AI composer, open the [Push presets editor](https://docs.pushwoosh.com/product/content/push-presets#create-push-content) and look for the **AI composer** button located just below the push message input field. ![AI composer button in Push presets editor](https://docs.pushwoosh.com/content-push-ai-composer-1.1.webp) In the window that opens, you’ll see a form with several input fields. ![AI composer form](https://docs.pushwoosh.com/content-push-ai-composer-1.webp) 1. **Enter keywords** *(Required)*\ Start by entering key terms related to your message, such as ***app, push notification, discount,*** etc. These keywords help guide the content generation. 2. **Target audience** *(Optional)*\ Specify who the message is for, e.g., ***“new users,” “active users,” or “users of the app.”*** This helps tailor the tone and content. 3. **Set the tone of voice** *(Optional)*\ Define the tone you want the message to have, such as ***professional, casual,*** or ***fun***. This helps ensure the notification aligns with your brand voice. 4. **Set the goal** *(Optional)*\ Clarify the purpose of the message, e.g., ***“get users to update the app”*** or ***“encourage a purchase.”*** This helps focus the messaging. 5. **Add the product description** *(Optional)*\ If your push notification is about a specific product, include a short description to provide more context for the generated content. 6. Once all relevant fields are filled out, click **Generate content** to receive a ready-to-use push notification message. 7. If you’re satisfied with the generated text, click **Copy to message** to insert it into your push notification. If you’d like a different version, click **Generate again** to create new text. Tip You can adjust any of the fields and regenerate content until you’re happy with the result. ## Example scenario: Notifying users about a new app update Imagine, you’ve just released a new version of your fitness app that includes performance improvements and a refreshed interface. You want to notify your active iOS users about the update using a friendly and engaging tone. To do this efficiently, you use the **AI composer**. Here’s how you fill out the form: * **Enter keywords:** *app update, new version, faster performance* * **Target audience:** *active users on iOS* * **Tone of voice:** *friendly and encouraging* * **Goal:** *encourage users to download the latest app version* * **Product description:** *A fitness app that helps users track workouts easily* After clicking **Generate content**, the AI returns a push notification like: > **Title:** Time to upgrade!\ > **Subtitle:** Get faster performance\ > **Message:** Update to the new version now! ![Example push notification](https://docs.pushwoosh.com/content-push-ai-composer-2.webp) You can fine-tune the message if needed or use it as-is and send it to your selected audience. --- # Translate with AI **URL:** https://docs.pushwoosh.com/product/content/push-presets/translate-push-content-with-ai/ Instantly translate your push notification content into multiple languages using AI-powered localization tools for faster, easier multilingual campaign management. --- The **Translate with AI** feature allows you to quickly localize your content by automatically translating it from the default language into [selected target languages](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push) using AI. This helps speed up the localization process and reduces manual work when managing multilingual content. Tip Before using **AI Translate**, make sure to [define the languages you want to use in your push](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push). Already have translations from a vendor or another preset? [Import them from a CSV or JSON file](https://docs.pushwoosh.com/product/content/push-presets/import-export-translations/) instead of generating new ones. ## How to translate with AI ### Step 1: Open the AI Translate option In the [Push presets editor](https://docs.pushwoosh.com/product/content/push-presets/), locate your push message content field. Underneath this field, click the **AI Translate** button to start the translation process. ![AI Translate button in Push presets editor](https://docs.pushwoosh.com/ai-translator-1.webp) Pushwoosh will use the current default language as the source and translate the content into the [target languages you’ve selected](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push). ### Step 2: Configure translation settings In the window that opens, check **Overwrite existing content** if you want the AI to replace any existing translations in the selected languages. Leave it unchecked to keep the ones you already have. ![AI Translate button in Push presets editor](https://docs.pushwoosh.com/content-push-ai-composer-3.webp) ### Step 3: Complete translation Click **Translate**. The message text will be automatically translated and inserted into the fields for the selected language version. ![AI translated content inserted into language fields](https://docs.pushwoosh.com/ai-translator-2.webp) --- # SMS and messenger presets **URL:** https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/ Create reusable content for SMS, RCS, LINE, and KakaoTalk, or build one rich message that works across several channels. --- SMS presets, LINE presets, Kakao templates, and rich messages live in **Content > Messenger presets**. Without rich messages, the page has the **SMS**, **LINE**, and **Kakao** tabs, and RCS is set up inside an SMS preset. The **LINE** tab appears only when LINE is enabled for your account. When rich messages are turned on for your account, the page has a tab per channel: **SMS**, **RCS**, **WhatsApp**, **Messenger**, **LINE**, and **Kakao**. The **RCS**, **WhatsApp**, **Messenger**, and **LINE** tabs list rich messages. A rich message builds one message for several channels at once. ## SMS presets Create reusable SMS content with personalization, several languages, and link shortening. To send over RCS, attach a Twilio RCS template to an SMS preset. [Learn more about SMS presets ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) ## LINE presets Create LINE text, image, carousel, and Flex messages in several languages. [Learn more about LINE presets ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) ## Kakao templates Create KakaoTalk message templates with several types and personalization. [Learn more about Kakao templates ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) ## Rich messages Build one message with buttons and cards and send it over RCS, WhatsApp, LINE, and Messenger, instead of building a preset for each channel. [Learn more about rich messages ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) --- # Kakao templates **URL:** https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/ Create and manage KakaoTalk message templates with multiple types, personalization, and approval workflows for campaigns. --- Kakao templates let you create and manage reusable KakaoTalk message templates directly in the Pushwoosh Control Panel. You can personalize messages with variables, choose from multiple template types, and submit templates for approval. Once created and approved, you can use these templates in campaigns to reach users via KakaoTalk. ## Available template types | | | | ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Basic (BA)** | Text-only template with optional highlight section. Best for simple notifications and straightforward messages. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#basic-ba-template) | | **Additional Information (EX)** | Template with main message and additional info section for structured data (like appointment time, location, or order details). Best for delivery notifications, order confirmations, and messages that require extra context. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#additional-information-ex-template) | | **Channel Addition (AD)** | Template with exactly one Add Channel button that redirects users to your KakaoTalk Channel. Best for growing your Kakao Channel audience and enabling future communications. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#channel-addition-ad-template) | | **Combined (MI)** | Template with Add Channel button plus up to 4 additional action buttons (up to 5 total). Best for interactive, service-related messages that require users to respond, confirm, reschedule, or access support. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#combined-mi-template) | ## Approval statuses After you submit a KakaoTalk template, it goes through review before it can be used in campaigns. You can see each template’s status in the **Template Management** list: | | | | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | | **REG (Registered)** | The template is saved in the system but has not yet been submitted for review. You can continue editing it and submit it when ready. | | **REQ (Requested)** | The template has been sent for review and is currently being reviewed by Kakao. | | **APR (Approved)** | The template is approved and can be used in campaigns and one‑time sends. | | **REJ (Rejected)** | The template was declined. Review the rejection notes, update the content to meet guidelines, and resubmit. | ![Kakao template management list showing approval statuses](https://docs.pushwoosh.com/content-kakao-presets-1.webp) ## Creating templates ### Basic (BA) template 1. Go to **Content > Messenger presets**, open the **Kakao** tab, and click **Create Template**. 2. From the **Template Type** dropdown, choose **Basic (BA) – Text only with optional highlight.** 3. Fill in template details: * **Template Name:** Enter a name to identify this template internally (e.g., *Order Notification*). * **Categories:** Enter one or more categories for this template (e.g., `notification`). Separate multiple categories with commas. 4. In the **Message Content** field, enter the body of your message. You can use up to 1,000 characters. You can personalize it with variables. For this, insert variables by clicking **+ Add Variable** and selecting from the available list, or type them directly in the format `#{variable}`. *Example:* ```plaintext Hello #{name}, Your order #{orderNumber} has been shipped. Expected delivery: #{date}. ``` ![Add variable interface in Kakao template editor](https://docs.pushwoosh.com/content-kakao-presets-2.webp) 5. (Optional) Add a highlight section to emphasize key information at the top of the message bubble: * **Subtitle**: Up to 18 characters. Variables are not allowed. * **Title**: Up to 23 characters (2 lines). Variables are allowed and must be from the available list. ![Highlight section configuration in Kakao template editor](https://docs.pushwoosh.com/content-kakao-presets-3.webp) 6. Check the **Live Preview** panel on the right to see how your message will appear to recipients. 7. Click **Save Template** in the top right. The template will be saved in REG (Registered) status. 8. Once you’re ready, navigate back to the **Template Management** list (**Content > Messenger presets**, **Kakao** tab). Select your newly created template to open the **Edit Template** view. Click the blue **Submit for approval** button. ![Submit for approval button in Kakao template editor](https://docs.pushwoosh.com/content-kakao-presets-4.webp) ### Additional information (EX) template The Additional Information (EX) template includes a structured main message and an optional additional info section. It’s ideal for sharing details like delivery status, order info, or instructions below the main message. You can add up to 5 buttons (Add Channel buttons are not allowed). Follow steps 1–5 from [Basic (BA) template](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#basic-ba-template) to set up the template name, categories, main message, variables, and highlight section. Then continue with the steps below: 1. Add supporting details below the main message. Variables are not allowed here. Max 500 characters. *Example:* ```plaintext Items in your order: - 1x Bluetooth headphones - 1x Protective case Questions? Contact support@example.com ``` 2. Add up to 5 action buttons. Click **Add Button** and provide: * **Title**: the button text * **Action/URL**: where the button leads (e.g., order tracking page) 3. Use the **Live Preview** panel to see how your message will appear. ![Additional Information template preview](https://docs.pushwoosh.com/content-kakao-presets-5.webp) 4. When you’re done, click **Save Template**. The template is saved in REG (Registered) status and not yet submitted. 5. Go to the **Template Management** page and click **Submit for approval** when you’re ready. ### Channel addition (AD) template The Channel Addition (AD) template is used when your goal is to prompt users to follow your KakaoTalk channel. This type of template is required to include exactly one Add Channel button. This template type is ideal for onboarding flows or opt-in requests, where channel subscription is the main CTA. Follow steps 1–5 from [Basic (BA) template](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#basic-ba-template) to set up the template type, name, categories, main message, variables, and highlight section. Then continue with the steps below: 1. Click **Add Button** in the Buttons section and enter a **Button Name** (up to 14 characters).\ *Example:* `Follow` The **Link Type** is set to `AC – Add Channel` by default. ![Add Channel button configuration](https://docs.pushwoosh.com/content-kakao-presets-6.webp) Important The template cannot be saved unless exactly one Add Channel button is present. 2. Use the **Live Preview** on the right to check how the message will appear. ![Add Channel button configuration](https://docs.pushwoosh.com/content-kakao-presets-7.webp) 3. Click **Save Template** to save it in REG (Registered) status. When ready, go to the **Template Management** page and click **Submit for approval**. ### Combined (MI) template The Combined (MI) template lets you include an Add Channel button first, followed by up to 4 additional action buttons. This type is useful when you want users to follow your KakaoTalk channel *and* offer extra links for related actions (like tracking, help pages, surveys, etc.). Follow steps 1–5 from [Basic (BA) template](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/#basic-ba-template) to set up the template type, name, categories, main message, variables, and highlight section. Then continue with the steps below: 1. (Optional) Add supporting details below the main message. Variables are not allowed here. Max 500 characters. *Example:* ```plaintext Items in your order: - 1x Bluetooth headphones - 1x Protective case Questions? Contact support@example.com ``` 2. Click **Add Button** and set the **Button Name** (max 14 characters), e.g. `Follow` or `Subscribe`. 3. Add up to four more buttons after the Add Channel button. Each additional button must include: * **Button Name**: e.g., `Track Order`, `Help`, `Shop Now`. * **Link Type**: choose from `WL – Web Link`, `AL – App/Web Link`, or `AC – Add Channel`. * **Mobile URL**: required. * **PC URL**: optional. *Example:* | Field | Value | | ----------- | ------------------------------------------------------- | | Button Name | `Track Order` | | Link Type | `WL – Web Link` | | Mobile URL | `https://example.com/track/#{orderNumber}` | | PC URL | `https://example.com/track/#{orderNumber}` *(optional)* | ![Combined template button configuration](https://docs.pushwoosh.com/content-kakao-presets-8.webp) 4. Use the **Live Preview** panel to see how your message will appear. ![Live Preview of Combined (MI) template with Add Channel and action buttons](https://docs.pushwoosh.com/content-kakao-presets-9.webp) 5. Click **Save Template**. The template is saved in REG (Registered) status and not yet submitted. 6. Go to the **Template Management** page and click **Submit for approval** when you’re ready. --- # LINE presets **URL:** https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/ Create and manage LINE message presets with multiple languages and formats. Craft text, image, carousel, and Flex messages for better communication across LINE channels. --- You can create and preview LINE message content directly in the **Pushwoosh Control Panel**. 1. Go to **Content > Messenger presets**. 2. Open the **LINE** tab. 3. Click **Create Line content**. ![Messenger presets page with the LINE tab selected and the Create Line content button highlighted](https://docs.pushwoosh.com/content-line-presets-create.webp) If [rich messages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) are turned on for your account, the **LINE** tab lists rich messages and has **Create rich message** instead of **Create Line content**. Pushwoosh makes a LINE copy of each rich message. LINE presets you created without the rich editor appear under **LINE presets made before rich messages**, and you edit them as described below. ## Define languages You can create localized LINE messages in multiple languages to ensure users receive content in their preferred language. For how multi-language messaging works across channels, see [Multi-language messaging](https://docs.pushwoosh.com/product/personalization/multi-language/). Start by creating your default message, which will be sent to all users unless a more specific language version is available. You can then add additional languages to provide localized content based on users’ language settings. To support multiple languages in a LINE preset: Note The language selector here only counts devices on the LINE platform. Language values set through user-level Tags aren’t tied to a device platform, so they don’t show up here. 1. In the message editor, click the **plus (+) icon** next to the **Default language** dropdown.\ ![Define languages](https://docs.pushwoosh.com/content-line-presets-1.webp) 2. From the list that appears, select the additional languages you want to include. 3. Enter localized content for each selected language. Use the left and right arrows next to the language dropdown to switch between languages and fill in the corresponding content fields. Example For example, if English is the default language and you want to support Spanish and German users, add those languages, enter the translations, and users will receive the message in their device language. Others will see the default English version. ## Select the message type In the editor, under **Type**, select the format of the message you want to send: * **Message**: A simple text-based message. * **Image**: A message that includes a single image. * **Image Carousel**: A scrollable set of multiple images. * **Flex message**: A fully customizable rich-format message defined using LINE’s Flex Message JSON schema. You can preview how your message will appear on iOS and Android devices in the panel on the right. ### Create a text message preset 1. Select **Message** as the message type. 2. Enter your message text in the **Content** field. ![Create text message content ](https://docs.pushwoosh.com/content-line-presets-2.webp) [Learn more about LINE text messages](https://developers.line.biz/en/docs/messaging-api/message-types/#text-messages) ### Create an image message preset Image messages let you send a single image to users. To do this, you need to provide two links: one for the full-size image and one for a smaller preview version. The preview image is what users see in the chat before opening the full image, so it should be smaller and load faster. Important LINE image messages do not support text. Only the image is delivered and any text content must be sent in a separate message. To create an image message: 1. Select **Image** under **Type**. 2. In the **Content** section, provide the following: * **Image URL:** The full-size image that will be displayed in the message. * **Preview image URL:** A smaller version of the image, shown in the chat bubble before the image expands. ##### Image requirements | Requirement | Image URL | Preview image URL | | :-------------------- | :-------------- | :---------------- | | **Protocol** | HTTPS | HTTPS | | **Max length** | 2000 characters | 2000 characters | | **Supported formats** | JPEG, PNG | JPEG, PNG | | **Max file size** | 10 MB | 1 MB | > **Important:** Both URLs need to be publicly accessible so LINE can load and show the images properly. ![Create image message content](https://docs.pushwoosh.com/content-line-presets-3.webp) [Learn more about LINE image messages](https://developers.line.biz/en/docs/messaging-api/message-types/#image-messages) ### Create an image carousel message preset The **Image Carousel** message type allows you to send multiple images in a horizontal, scrollable format. Each image acts as an interactive panel and can trigger an action (e.g., open a URL) when tapped. To create image carousel message content: 1. Select **Image Carousel**. 2. In the **Content** section, set the following: * **Alternative text**. Enter a fallback message shown in clients that don’t support rich messages (e.g., “View our latest products.”). * **Image URL.** Add one or more image URLs. Each image will appear as a separate panel in the carousel. 3. To add more images, click the **+ Image** button. **Note:** You can add a minimum of 1 and up to 10 images. ##### Image requirements Each image must meet the following criteria: | | | | ------------------ | --------------- | | **Protocol** | HTTPS only | | **Max URL length** | 2000 characters | | **Format** | JPEG or PNG | | **Max file size** | 10 MB per image | > **Important:** Make sure all images are publicly accessible so LINE can retrieve and display them properly. ![Create image carousel message content ](https://docs.pushwoosh.com/content-line-presets-4.webp) For more information on formatting and supported actions, refer to the [LINE Image Carousel documentation.](https://developers.line.biz/en/docs/messaging-api/message-types/#carousel-template) ### Create a Flex message preset Flex messages offer a highly customizable way to design message layouts in LINE using JSON. They are ideal for delivering structured content such as product details, promotions, or interactive menus. To create a Flex message: 1. Under **Type**, select **Flex message**. 2. In the **Content** section, complete the following fields: * **Alternative text:** Enter fallback text to display on devices that do not support Flex messages. This text should briefly describe the message content. * **Content (JSON):** Provide a valid Flex Message JSON payload that defines the structure and appearance of the message. ![Create Flex message content](https://docs.pushwoosh.com/content-line-presets-5.webp) Tip Use the official [LINE Flex Message Simulator](https://developers.line.biz/flex-simulator/) to build and preview your JSON layout before copying it into the editor. For detailed information on supported properties and layout options, refer to the [LINE Flex Message Reference](https://developers.line.biz/en/docs/messaging-api/using-flex-messages/). ## Preview and save the message Once you’re satisfied with the content and format: * Click **Save** to apply your changes. * Click **Cancel** to discard your edits. ## Manage LINE presets Saved presets appear under **Content > Messenger presets** on the **LINE** tab. The list shows each preset’s title, category, and last updated date. Use the search bars at the top to find content by name or category. From this page, you can edit, delete, or create new LINE content. ![LINE presets management interface showing list of created LINE content](https://docs.pushwoosh.com/content-line-presets-list.webp) A preset with a **Rich message** badge is a copy of a [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/). You can’t edit or delete it or change its category here. Click it to open the rich message in its editor. If rich messages aren’t turned on for your account, the copy opens read-only in its own form. ### LINE categories Each LINE message has a category shown in the **Category** column. To change it, click the current category next to the LINE title, then: * **Assign a category:** select one from the dropdown, or create a new one. * **Remove the category:** select **Default**. Note [Content categories](https://docs.pushwoosh.com/product/content/#content-categories) apply to all types of content within a single project, except push. --- # Rich messages **URL:** https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/ Build one message with buttons, cards, or carousels and send it over RCS, WhatsApp, LINE, and Messenger from one preset. --- Build a message with buttons, media, cards, or a carousel once and send it over RCS, WhatsApp, LINE, and Facebook Messenger. Pushwoosh checks the content against each channel while you edit and turns it into a preset for each channel, so you don’t build the same message four times. Note Rich messages are turned on per account. If you don’t see the **RCS**, **WhatsApp**, and **Messenger** tabs in **Content > Messenger presets**, contact your Pushwoosh account manager. ## Before you start Pushwoosh creates a copy of the message only for channels set up in the project. A channel that isn’t set up gets the **Has errors** status, and nothing is sent there. * **RCS** (Rich Communication Services) shows messages from your brand in the phone’s built-in messaging app, with buttons, cards, or media instead of plain text. To send it, connect Twilio with your own account credentials and a Messaging Service. A Twilio number without a Messaging Service can’t send RCS. See [RCS messages through Twilio](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/). * **WhatsApp**: [connect your WhatsApp Business account](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) for the project. * **LINE**: [configure the LINE platform](https://docs.pushwoosh.com/product/messaging-channels/line/) for the project. * **Facebook Messenger**: [connect your Facebook Page](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/). ## Create a rich message 1. Go to **Content > Messenger presets**. 2. Open the tab of the channel you build the message for: **RCS**, **WhatsApp**, **Messenger**, or **LINE**. The editor opens with this channel selected in the **Channels** strip. 3. Click **Create rich message**. 4. Click **Enter preset name** next to **Rich message** at the top and enter a name for the message. ## Add languages The message starts with the **Default language**, which is sent to users with no matching language. To add translations: 1. Click the **plus (+) icon** next to the language dropdown. 2. Select the languages you want to add. 3. Switch between languages in the dropdown and fill in the content for each one. Every language has its own content type and fields. The channel status is the worst one across all languages: if one language has errors in a channel, the whole message has errors there. ## Choose the content type Select a **Type** in the **Content type** block before you fill in anything else. | Type | What it is | Use it when | | :------------------- | :--------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------- | | **Text** | Plain text with no media or buttons. | You only need to tell users something. | | **Media** | Text with an image, video, document, or audio file. | You want to send a file together with the text. | | **Quick reply** | Text with reply buttons. | You want users to answer with one tap. In a journey, each button gets its own branch, so users continue by their answer. | | **Call to action** | Text with buttons that open a link or call a phone number. | You want users to open a page or call a number straight from the message. | | **List picker** | Text with a list of options. | You want users to choose one option from a list. Works only in WhatsApp, inside the 24-hour window. | | **Card** | One block with media, a title, text, and buttons. | You present one item with a picture and buttons. | | **Carousel** | Two to ten cards in one message. | You present several items at once. | | **Custom LINE Flex** | Your own LINE Flex layout. | You already have a LINE Flex layout. Works only in LINE. | **List picker** and **Custom LINE Flex** work in one channel each. The other types work in all four channels: RCS, WhatsApp, LINE, and Messenger. A channel where a type doesn’t work gets **Has errors**, and Pushwoosh sends nothing there. Next to **Type**, **WhatsApp category** sets the category of the WhatsApp template. See [Get the WhatsApp template approved](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#get-the-whatsapp-template-approved). ## Fill in the message Enter the content in the blocks that appear for the type you chose. Each type below lists its blocks and the limits that the editor checks in each channel. Content over a limit gives the channel the **Has errors** status. See [Check channel compatibility](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#check-channel-compatibility). ### Text Use it for a plain message without media or buttons. It works in all channels. Caution Don’t use Liquid control tags such as `{% if %}` or `{% for %}` in the **Body**. RCS and WhatsApp send the text as a provider template, which doesn’t support them. A message with these tags gets **Has errors** for RCS and can’t be submitted as a WhatsApp template. 1. In **Type**, select **Text**. 2. Type the message text in **Body**. You can insert emoji and personalization placeholders. **Body** limits: 1,600 characters in RCS, 5,000 in LINE, and 2,000 in Messenger. ### Media Use it to send an image, video, document, or audio file with text. You choose the type of file, then add a link to it. 1. In **Type**, select **Media**. 2. Enter the **Body**. 3. Select the **Media type**: **Image**, **Video**, **Document**, or **Audio**. 4. Set the file link in **Media URL**: * For an image, click the attachment icon next to the field and pick a JPEG or PNG file from the media library, or paste an `https://` link. * For a video, document, or audio file, paste an `https://` link. The media library holds images only. 5. For a video, set a **Video poster URL** the same way as an image. ![Media rich message with the Media URL field and its attachment icon, the Media type dropdown, and an image selected](https://docs.pushwoosh.com/content-rich-messages-6.webp) **Body** limits: 1,600 characters in RCS and 2,000 in Messenger. Note LINE shows a video only with a **Video poster URL**, so without it the LINE badge has errors. LINE doesn’t send audio or documents: with these media types, the LINE badge has errors and nothing is sent to LINE. In the **Media** type, LINE doesn’t show the **Body**, and Messenger sends the file without a caption. The same steps set **Header media URL** and the media of cards. ### Quick reply Use it when you want users to answer with one tap. In a journey, each button gets its own branch, so you can continue the journey by the user’s answer. 1. In **Type**, select **Quick reply**. 2. Enter the **Body**. 3. Click **Add button** and enter the button **Title**. 4. Repeat step 3 for each button. ![Three quick reply buttons, each with a title, and the Add button control](https://docs.pushwoosh.com/content-rich-messages-15.webp) Limits: * **Buttons:** 1–4 in RCS, 1–13 in LINE and Messenger. * **Button title:** 25 characters in RCS, 20 in LINE and Messenger. Note Pushwoosh gives every button an ID when you save. Journeys branch on that ID, so renaming a button later keeps its branch in journeys that already use it. **Quick reply**, **Call to action**, and **List picker** also have the **Header text**, **Header media URL**, and **Footer** fields. Only WhatsApp sends them. RCS, LINE, and Messenger leave them out. ### Call to action Use it to send users to a link or let them call a phone number straight from the message. 1. In **Type**, select **Call to action**. 2. Enter the **Body**. 3. Click **Add button**. 4. Select the **Type of action**: **Open URL** or **Call phone**. 5. Enter the button **Title**. 6. Fill in the field for the action: * For **Open URL**, enter the **URL**. * For **Call phone**, enter the **Phone number** in international format, for example `+1234567890`. ![Call to action rich message with two buttons: the Type of action, Title, and URL or Phone number fields filled in](https://docs.pushwoosh.com/content-rich-messages-7.webp) Limits: * **Buttons:** 1–4 in RCS, 1 or more in LINE, 1–3 in Messenger. * **Button title:** 25 characters in RCS, 40 in LINE, 20 in Messenger. * **Body:** 1,600 characters in RCS and 640 in Messenger. The editor doesn’t check the LINE body. ### List picker Use it to let users choose one option from a list. 1. In **Type**, select **List picker**. 2. Enter the **Body**. 3. In the **List** block, enter the **List button text**. 4. Click **Add item**. 5. Enter the **Item name** and, optionally, the **Item description**. 6. Repeat steps 4 and 5 for each item. ![List picker rich message with the list button text and two list items, each with a name and a description](https://docs.pushwoosh.com/content-rich-messages-8.webp) Note Only WhatsApp shows a list, and only inside the 24-hour window, because a list can’t be a WhatsApp template. RCS, LINE, and Messenger get **Has errors**, and nothing is sent there. ### Card and carousel Use a card for one block with media, text, and buttons. Use a carousel to show two to ten cards in one message. 1. In **Type**, select **Card** or **Carousel**. 2. For a carousel, enter the **Body** in the **Message** block. 3. Click **Add card** for each card, up to ten. 4. Fill in each card: * **Media URL** and **Media type** * **Card title** * **Card body** (optional) * **Footer** (optional) * Buttons: **Quick reply**, **Open URL**, or **Call phone** ![First card of a carousel with the media URL, card title, card body, footer, and two buttons](https://docs.pushwoosh.com/content-rich-messages-9.webp) 5. To change how the card looks in RCS, select **RCS** in the **Channels** strip, then set **Orientation**, **Height**, and **Thumbnail alignment** under **RCS card layout**. Leave the fields at **Default** to use the RCS default layout. ![Add card button and the RCS card layout block with Orientation, Height, and Thumbnail alignment set to Default](https://docs.pushwoosh.com/content-rich-messages-12.webp) #### Card rules by channel The channels accept different cards: * **Carousel Body:** RCS and Messenger don’t send it. * **RCS carousel:** every card needs an image or a video, a **Card body**, and one or two buttons. The buttons must be of the same types in the same order on every card. RCS doesn’t show the card **Footer** in a carousel. In a single card, the footer is shown under the body and can’t contain placeholders. * **Messenger:** every card needs a **Card title**, and card media must be an image. Messenger doesn’t show the card **Footer**. * **LINE:** card media must be an image. The editor also checks these limits: * **Cards in a carousel:** 2–10 in RCS, LINE, and Messenger. * **Card title:** 200 characters in RCS and 80 in Messenger. The editor doesn’t check it in LINE. * **Card body:** in RCS, 1,600 characters in a single card, and 160 characters for the title and body together in a carousel. In Messenger, 80 characters. The editor doesn’t check it in LINE. * **Buttons on a card:** in RCS, 0–4 on a single card and 1–2 in a carousel. In Messenger, 0–3. The editor doesn’t check it in LINE. ### Custom LINE Flex Use **Custom LINE Flex** when you already have a LINE Flex layout. 1. In **Type**, select **Custom LINE Flex**. 2. Paste a Flex container into **Flex JSON**. The container type must be `bubble` or `carousel`. ![Custom LINE Flex rich message with a Flex container pasted into the Flex JSON field](https://docs.pushwoosh.com/content-rich-messages-10.webp) LINE sends it as is. RCS, WhatsApp, and Messenger get **Has errors**, and nothing is sent there. ### LINE chat-list text For cards, carousels, calls to action, and Custom LINE Flex messages, LINE takes the chat-list and notification text from the first of these that is filled in: 1. The **Body**. 2. The title or body of the first card. 3. The message name. A Custom LINE Flex message has no **Body** and no cards, so LINE shows the message name. LINE gets at most 400 characters of this text, and the rest is cut off. ## Set the SMS fallback Phones without RCS get the RCS message as an SMS. Enter its text in **SMS text** in the **SMS fallback** block at the bottom of the form. The block appears only while **RCS** is selected in the **Channels** strip. If **SMS text** is empty, the SMS carries the **Body**. With both empty, RCS gets **Has errors**. The SMS can be up to 1,600 characters. To add a short link, paste the long link into **Shorten a link** under the field and click **Shorten & insert**. Pushwoosh adds the short link to the end of the SMS text. ![SMS fallback block with the SMS text filled in and a long tracking link entered in Shorten a link next to the Shorten and insert button](https://docs.pushwoosh.com/content-rich-messages-11.webp) WhatsApp, LINE, and Messenger have no text replacement. A channel that can’t show the message gets **Has errors**, and nothing is sent there. ## Check channel compatibility The **Channels** strip under the header shows one badge per channel. Pushwoosh rechecks the message when you leave a field or change a dropdown. | Badge color | Status | What it means | | :---------- | :------------------- | :------------------------------------------------------------------------------------- | | Green | Compatible | The channel sends the message as designed. | | Orange | Partially compatible | WhatsApp only: the message goes out only inside the 24-hour window, not to everyone. | | Red | Has errors | Nothing is sent to this channel until you fix the errors. | | Gray | Not checked | The message hasn’t been checked yet, or the check failed. Edit a field to check again. | Click a badge to see the issues for that channel under the strip and to preview the message in that channel on the right. Each issue names the field and the rule it breaks. When you select a channel, fields that it doesn’t deliver get a note next to their name in the form, for example **(not delivered on RCS)**, and the preview leaves them out. In this example, RCS has errors because RCS isn’t set up in the project: ![Rich message editor header with RCS selected and an RCS is not set up in this application issue under the Channels strip](https://docs.pushwoosh.com/content-rich-messages-2.webp) ## Save the rich message Click **Save**. Pushwoosh saves the message and, for every channel without **Has errors**, creates or updates its copy: * **RCS**: an SMS preset with the same name. Pushwoosh creates an RCS template in your Twilio account and attaches it to the preset. Phones without RCS get the [SMS fallback](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#set-the-sms-fallback). * **WhatsApp**: a WhatsApp preset with the same name, available in the **WhatsApp** journey element and in one-time WhatsApp messages. * **LINE**: a LINE preset with the same name. * **Messenger**: a Messenger preset with the same name, available in the **Facebook Messenger** journey element. Each copy has a **Rich message** badge and appears in these places: * **RCS:** on the **SMS** tab, because the RCS copy is an SMS preset. * **LINE:** on the **LINE** tab under **LINE presets made before rich messages**, together with LINE presets created without the rich editor. The rich messages themselves are listed above it. The section appears only if the project has such a preset. Without it, the **LINE** tab lists only the rich messages and doesn’t show the LINE copies. You manage a copy through its rich message: * **Edit a copy:** click it or its **Edit** action. The rich message opens in the editor. * **Delete a copy:** delete the rich message. You can’t delete a copy on its own, and Pushwoosh deletes all its copies with the message. ![LINE tab of Messenger presets filtered by name, listing two rich messages and the Create rich message button](https://docs.pushwoosh.com/content-rich-messages-3.webp) Caution A channel with **Has errors** keeps its last copy as it was. If a journey already sends that copy, it keeps sending the old content until you fix the errors and save again. ## Get the WhatsApp template approved Outside the 24-hour window that a user’s last message opens, WhatsApp delivers only templates approved by Meta. Until the rich message has an approved template, Pushwoosh sends it on WhatsApp only to users who wrote to you in the last 24 hours, and the **WhatsApp** badge stays orange. 1. Next to **Type**, select the **WhatsApp category**: **Marketing** or **Utility**. Meta prices and reviews templates by category. 2. If you haven’t saved the message yet, [save it](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#save-the-rich-message). **Submit for WhatsApp approval** is available only for a saved message. 3. If you saved the message in step 2, open it again from the **WhatsApp** tab, because saving closes the editor. 4. Click **Submit for WhatsApp approval**. Pushwoosh saves your changes and sends a template for every language to Meta. If the button is unavailable, see [When you can’t submit](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#when-you-cant-submit). ![Rich message editor with the Submit for WhatsApp approval button highlighted and the WhatsApp category field next to Type](https://docs.pushwoosh.com/content-rich-messages-16.webp) ### Check the review state The line under the **Channels** strip shows the review state after **WhatsApp template:**. Before you submit, it reads **not submitted**: ![Rich message editor with the WhatsApp badge selected, the 24-hour window issue, the Submit for WhatsApp approval button, and WhatsApp template: not submitted](https://docs.pushwoosh.com/content-rich-messages-4.webp) The state changes as Meta reviews the template: | State | What it means | | :-------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------- | | **not submitted** | The template hasn’t been sent to Meta. WhatsApp gets the message only inside the 24-hour window. | | **pending review** | Meta is reviewing the template. Until it’s approved, WhatsApp gets the message only inside the 24-hour window. | | **approved** | WhatsApp sends the template to any user. | | **the latest change is pending review, the approved version is sent** | You edited and resubmitted an approved message. Outside the 24-hour window, users get the previously approved version. | | **rejected** | Meta rejected the template, and the reason follows the state. Fix the content and submit it again. | Pushwoosh checks the review state with Meta every few minutes. To check right away, click **Refresh status**. With several languages, the state shown is the one furthest from approval. ### When you can’t submit **Submit for WhatsApp approval** is unavailable when: * WhatsApp isn’t set up in the project. * the type is **Carousel**: a carousel goes out on WhatsApp only inside the 24-hour window. * WhatsApp can’t build a template from the type, for example **List picker** or **Custom LINE Flex**. For a carousel or a type that can’t be a template, the editor shows the reason under the review state. ### Edit an approved message Editing an approved message doesn’t stop it: outside the 24-hour window, the approved version keeps going out until Meta approves the new one. Submit the message again after each edit. ## Send a rich message in a journey Select the copy in the element of its channel: * **RCS**: in the **SMS** element, select the SMS copy as the preset. See [Branch on RCS reply buttons](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#branch-on-rcs-reply-buttons). * **LINE**: select the LINE copy as the preset. See [Branch on reply buttons](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/line/#branch-on-reply-buttons). * **WhatsApp**: select the rich message in the **Content** dropdown, where it has a **Rich message** badge. See [Use a rich message](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#use-a-rich-message). * **Facebook Messenger**: select **Preset**, then the Messenger copy. See [Branch on reply buttons](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#branch-on-reply-buttons). The element page describes the rest: branches for quick reply buttons and the **Rejected by provider** branch for messages the provider refuses. ## Send a rich message as a one-time WhatsApp message In a one-time WhatsApp message, select the rich message in the template dropdown on the **Content** step. See [Send a rich message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#send-a-rich-message). An approved rich message goes to any audience. Without an approved template, it goes only to contacts with an open 24-hour window, and the audience and sending time are fixed. ## Manage rich messages Rich messages are listed on the **RCS**, **WhatsApp**, **Messenger**, and **LINE** tabs, each with a badge for every channel. A channel tab lists the rich messages that this channel can send. ![RCS tab of Messenger presets with three rich messages and a compatibility badge for RCS, WhatsApp, Messenger, and LINE on each](https://docs.pushwoosh.com/content-rich-messages-1.webp) Open the **⋮** menu of a message to: * **Edit** it. * **Duplicate** it. The duplicate gets its own copies in every channel without **Has errors**. * **Delete** it. Pushwoosh deletes its copies too. You can’t delete a rich message while a journey sends one of its copies: remove it from the journey first. --- # SMS presets **URL:** https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/ Create reusable and customizable SMS content with personalization, multi-language support, and automatic link shortening. Build effective SMS campaigns with tracking and engagement insights. --- **SMS presets** are reusable SMS messages you prepare once and send again in [SMS campaigns](https://docs.pushwoosh.com/product/messaging-channels/sms) and Customer Journeys. Personalize the text for each user, adapt it to different languages, or fill it with products and articles instead of writing everything by hand. ## Create an SMS preset 1. Go to **Content > Messenger presets**. 2. Open the **SMS** tab. 3. Click **Create SMS content**. ![Messenger presets page with the SMS tab selected and the Create SMS content button highlighted](https://docs.pushwoosh.com/content-sms-presets-16.webp) 4. Choose a content type: * **Regular SMS:** a message you write yourself, optionally personalized with tags. * **Product Recommendations:** a message built from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog), with products picked by hand, pulled by a catalog rule (category and sort order), pulled by a recommendation strategy (for example, bestsellers or recently viewed), or fetched from the recipient’s abandoned cart. * **News Feeds:** a message built from a registered news feed with the latest or trending articles, optionally filtered to one feed and one category. Important The content type is fixed when you create the preset and can’t be changed afterward. 5. Enter a clear, descriptive name for the preset. Continue with [Add languages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-languages), then build content for the type you chose. ## Add languages You can create the same SMS preset in multiple languages. 1. Compose the default-language message first. 2. Click the **+** icon at the top of the editor. ![Select languages modal with a search field and a checklist of languages, showing Spanish and German checked](https://docs.pushwoosh.com/content-sms-presets-1.webp) 3. Select the additional languages you need. Note The language selector here only counts devices on platforms that receive SMS. Language values set through user-level Tags aren’t tied to a device platform, so they don’t show up here. 4. Switch between languages in the dropdown and fill in the message for each one. For example, if your default language is English and you add Spanish and German, users with Spanish or German phone settings receive the message in their language. Everyone else receives the English version. Note The default message goes to all users unless a version in their language is available. Add more languages to match users’ device language settings. For how multi-language messaging works across channels, see [Multi-language messaging](https://docs.pushwoosh.com/product/personalization/multi-language/). ## Build content by type The editor fields depend on the content type you chose when creating the preset. Tip Don’t forget an opt-out line in marketing SMS, for example “Reply STOP to unsubscribe”. It’s required in many countries. ### Regular SMS **Regular SMS** is a plain-text message you type in the **Message** field. Personalize it with user data or add a tracked link if you need to. 1. In the **Message** field, type your SMS text. 2. Click **Emoji** above the field to insert an emoji. 3. To personalize the message, follow the steps in [Personalize SMS content](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#personalize-sms-content) below. 4. To add a tracked, shortened link, paste the URL into **Shorten a link** and click **Shorten & insert**. To skip shortening and tracking, paste the URL directly into the **Message** field instead. Below the **Message** field, the editor shows the character and segment count. ![Regular SMS editor with the Message field filled in, the Personalization and Emoji toolbar, and Shorten a link filled in](https://docs.pushwoosh.com/content-sms-presets-25.webp) #### Personalize SMS content Insert user data into the message so each recipient sees their own name, city, or other details you have stored. 1. Click **Personalization** above the message field. 2. Select a tag from the dropdown. Available tags depend on the user data you have collected. [Learn more about tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) 3. Select a format modifier for how the tag value appears: * **CapitalizeFirst:** the first letter is uppercase and the rest lowercase. For example, “john doe” becomes “John doe”. * **CapitalizeAllFirst:** the first letter of each word is uppercase. For example, “john doe” becomes “John Doe”. * **UPPERCASE:** all letters uppercase. For example, “john doe” becomes “JOHN DOE”. * **Lowercase:** all letters lowercase. For example, “JOHN DOE” becomes “john doe”. * **Regular:** keeps the original formatting from your data. For example, “john doe” stays “john doe”. 4. In **Default Tag Value**, enter the fallback text when the tag has no value for a user. For example, for `{City}`, enter “your city”. 5. Click **Insert**. The tag appears in the message as a placeholder. At send time, Pushwoosh replaces it with each user’s value. If a user has no value for that tag, the **Default Tag Value** from step 4 is used instead. ![SMS personalization panel with the City tag, CapitalizeFirst modifier, and default value filled in before clicking Insert](https://docs.pushwoosh.com/content-sms-presets-2.webp) For example, you insert `{City}` with default value “your city” into the message “Explore, dine, and discover in `{City}`. Start now!” * **City has a value:** “Explore, dine, and discover in Chicago. Start now!” * **City is empty:** “Explore, dine, and discover in your city. Start now!” ### Product Recommendations **Product Recommendations** pulls product names, prices, and links from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog) into the SMS. Use it to promote a fixed set of products, highlight bestsellers or new arrivals, or remind users about items left in their cart. #### Compose the message Write the SMS text for the products your content source will return. Start with the single **Message** field (**Items** defaults to 1 on a new preset). To include two or more products, set **Items** in **Content source**, then fill **Intro**, **Item template**, and **Outro**. ##### One item 1. In the **Message** field, type your SMS text. Click **Title**, **Price**, **Category**, or **Link** to insert a product placeholder like `{{product.title}}` at the cursor. 2. To add a tracked, shortened link, paste the URL into **Shorten a link** and click **Shorten & insert**. ![Product Recommendations editor with a single Message field, product field buttons, and Shorten a link](https://docs.pushwoosh.com/content-sms-presets-33.webp) ##### More than one item Set **Items** to 2 or more in **Content source** below to split the message into three blocks around the repeated product list. 1. Fill in **Intro**. This block is sent once, before the products. 2. Fill in **Item template**. This block is repeated for each product. Click **Title**, **Price**, **Category**, or **Link** to insert a product field. Each inserts a placeholder like `{{product.title}}` at the cursor. 3. Choose **Separator between items**: **comma** puts products on one line (`Sneakers, Hoodie`), **new line** puts each product on its own line. 4. Fill in **Outro**. This block is sent once, after the products. Click **Title**, **Price**, **Category**, or **Link** here too to insert a product placeholder. Intro, items, and outro are joined with a new line. 5. To add a tracked, shortened link, paste the URL into **Shorten a link** and click **Shorten & insert**. ![Product Recommendations editor with Intro, Item template, Outro, and Shorten a link](https://docs.pushwoosh.com/content-sms-presets-26.webp) #### Choose a content source 1. Scroll to **Content source** at the bottom of the editor. 2. Set **Source** to one of four options: * **Manual:** hand-pick up to 3 products. * **Catalog rule:** fetch products by category and sort order. * **Recommendations:** fetch products by a recommendation strategy. * **Abandoned cart:** fetch products from the recipient’s abandoned cart. Configure the source you chose: ##### Manual 1. Click **Add a product**. 2. Search your catalog and select a product. Repeat to add up to 3 products. 3. To remove a product, click **Remove** next to it. 4. Before reusing an older preset, click **Refresh from catalog**. Prices and titles are frozen at pick time. ![Manual Content source with Add a product, selected products list, and Refresh from catalog](https://docs.pushwoosh.com/content-sms-presets-28.webp) ##### Catalog rule 1. Set **Category** and **Sort by**. 2. Set **Items** to how many products to include (up to 3 per message). 3. Optionally, under **Pinned rows**, hand-pick products to lead the message before the rule fills the remaining slots. 4. Optionally check **Trim product title to** a fixed character count to keep the message inside SMS length limits. 5. Set what happens **if the feed returns nothing**: skip sending to that recipient, or send a fallback text. ![Catalog rule source panel with a live product fetch and Pinned rows, without the MMS attachments section](https://docs.pushwoosh.com/content-sms-presets-22.webp) ##### Recommendations 1. Set **Strategy** to how products are picked: * **Bestsellers (7 or 30 days):** products with the most units sold in the last 7 or 30 days. Needs `PW_OrderCreated` and `PW_OrderUpdated` events with an `items` array. * **Newest:** latest products added to the catalog. Ranks from catalog data only. * **Back in stock:** products that recently came back in stock. Ranks from catalog stock changes. * **Price drop:** products with a recent price decrease. Ranks from catalog price changes. * **Bought together:** products other users viewed, added to cart, or bought alongside an anchor product in the last 30 days. Needs product activity events and a device tag that holds the anchor product ID. * **Recently viewed:** products that other users interacted with alongside the last 10 products this recipient interacted with, based on the last 30 days. Products the recipient already interacted with are left out. Needs events from the SDK or API with a `userId` and a product ID (`product_id`, `productId`, `item_id`, `sku`, or a `products` array) that matches the product `id` in your catalog. **Bestsellers**, **Bought together**, and **Recently viewed** need product events from your app, website, or backend, the same ones that power the [Products block in email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products). See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/) for what to send. ![Source set to Recommendations and Strategy set to Bestsellers, 30 days](https://docs.pushwoosh.com/content-sms-presets-19.webp) 2. Set **Category** to filter which products the strategy can pick from, and **Items** to how many products to include (up to 3 per message). If the strategy returns nothing, the plain catalog rule fills the message instead of sending it empty. ![Category and Items fields with the fallback warning shown when the strategy returns nothing](https://docs.pushwoosh.com/content-sms-presets-32.webp) 3. Optionally, pin products under **Pinned rows**. Pinned rows count against **Items**: the **Add a product** picker blocks you from selecting more than **Items** to begin with, showing 0 slots left once you hit the cap. If you lower **Items** below the number of rows already pinned, the extra pinned rows don’t send. Pin a product the strategy would also return and the row repeats, since the rule can’t exclude a pinned product from its own results. ![Pinned rows section with hand-picked products and a duplicate-product warning when a pinned item matches the strategy result](https://docs.pushwoosh.com/content-sms-presets-29.webp) 4. Optionally check **Trim product title to** a fixed character count. 5. Set what happens **if the feed returns nothing**: skip sending to that recipient, or send a fallback text. ##### Abandoned cart Pushwoosh fetches each recipient’s own cart when the message goes out, not in advance. Set **Items** to how many products to fetch (up to 3). Because the products depend on who’s receiving the message, there’s no preview and **Pinned rows** aren’t available. Unlike **Catalog rule** and **Recommendations**, there’s no **If the feed returns nothing** option here — Abandoned cart has no fallback control for a recipient with an empty cart. ![Abandoned cart source with the Items field](https://docs.pushwoosh.com/content-sms-presets-21.webp) ### News Feeds **News Feeds** pulls article titles, summaries, and links from an RSS or Atom feed into the SMS. Use it to share the latest news, trending stories, or updates from a specific category. #### Compose the message The message fields depend on **Articles** in **Content source** below. On a new preset, **Articles** defaults to 1, showing a single message field. Set it to 2 or more, and the field splits into **Intro**, **Article template**, and **Outro** blocks. Below the message fields, the editor shows an estimated character and segment count once at least one article matches the feed rule. ##### One article 1. In the **Message** field, type your SMS text. Click **Title**, **Summary**, **Category**, **Author**, or **Link** to insert an article field. Each inserts a placeholder like `{{news.title}}` at the cursor. There’s no publish-date field. 2. To add a tracked, shortened link, paste the URL into **Shorten a link** and click **Shorten & insert**. ![News Feeds Message field with article field inserts for Title, Summary, Category, Author, and Link, and the Shorten a link section](https://docs.pushwoosh.com/content-sms-presets-27.webp) ##### More than one article Set **Articles** to 2 or more in **Content source** below to split the message into **Intro**, **Article template**, and **Outro** blocks around the repeated article list. 1. Fill in **Intro**. This block is sent once, before the articles. 2. Fill in **Article template**. This block is repeated for each article. Click **Title**, **Summary**, **Category**, **Author**, or **Link** to insert an article field. Each inserts a placeholder like `{{news.title}}` at the cursor. 3. Set **Separator between articles** to comma or new line. 4. Fill in **Outro**. This block is sent once, after the articles. Click **Title**, **Summary**, **Category**, **Author**, or **Link** here too to insert an article field. Intro, articles, and outro are joined with a new line. 5. To add a tracked, shortened link, paste the URL into **Shorten a link** and click **Shorten & insert**. ![News Feeds editor with Intro, Article template, Outro, and Shorten a link](https://docs.pushwoosh.com/content-sms-presets-34.webp) #### Choose a content source Feeds must be registered on the [**News feeds**](https://docs.pushwoosh.com/product/content/news-feeds/) page for your application. This panel only picks a feed. It doesn’t register one. If none exist yet, a warning banner appears — register a feed on that page first, using **Add feed**. 1. Scroll to **Content source** at the bottom of the editor. 2. Set **Feed** to one registered feed, or **All feeds** to pull from every feed in the project. 3. Set **Rubric** to one category of the selected feed, or leave it as **Any rubric**. Switching the feed resets this field, since categories belong to the feed they came from. 4. Set **Ranking**: * **Newest:** the latest articles by publish date from the feed. * **Trending:** the articles with the most recent opens on your site and clicks in your emails. How Trending works **Trending** picks articles that people are reading right now. Pushwoosh counts two signals: * **Site opens:** [`PW_SiteOpened`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_siteopened) fires when someone opens a page on your site. Install the Pushwoosh Web SDK on your site to collect this signal. * **Email clicks:** [`PW_EmailLinkClicked`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emaillinkclicked) fires when someone clicks an article link in your email. No extra setup beyond sending emails through Pushwoosh. You don’t send these events yourself. Without the Web SDK on your site, ranking uses email clicks only, which gives a weaker signal. When there isn’t enough recent activity yet, **Trending** falls back to **Newest**. 5. Set **Articles** to how many articles to include (up to 3 per message). ![Content source panel with Feed, Rubric, Ranking, and Articles fields for a News Feeds preset](https://docs.pushwoosh.com/content-sms-presets-30.webp) 6. Under **Pinned articles**, click **Add an article** to hand-pick articles that lead the message. A pinned article is excluded from what the feed fetches, so it’s never repeated. Titles are frozen at pick time. The send doesn’t re-read them. 7. Optionally check **Trim article title to** a fixed character count. 8. Under **If the feed returns nothing**, choose what happens when the rule finds no articles: * **Do not send to this recipient:** skip the send for that user. * **Send this text instead:** send a fallback message you type in the field that appears. If you leave that field blank, Pushwoosh skips the send to that recipient instead of sending an empty SMS — fill in real fallback text if you want a message to go out. To check the fallback text and content source settings you saved, see [Review what’s saved into the preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#review-whats-saved-into-the-preset). ![Pinned articles list with a Remove option, Trim article title checkbox, and If the feed returns nothing radio options](https://docs.pushwoosh.com/content-sms-presets-24.webp) ## Review what’s saved into the preset For Product Recommendations and News Feeds presets, Pushwoosh saves your **Content source** settings as a [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/) template that fetches products or articles at send time. Reviewing it helps you check your filters, fallback text, and pinned items, especially when the preview is empty. The panel is read-only — change settings through **Content source** instead. Note The template runs only if [Liquid content templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) are enabled for your account. If they aren’t, recipients get the raw template text instead of a rendered message. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you’re unsure whether your account has this enabled. 1. Scroll to **Content source** and click **Show what is saved into the preset**. 2. Review the Liquid block Pushwoosh stores for this preset. It includes the request that fetches your products or articles, your content source settings, and any fallback message text. 3. Click **Hide what is saved into the preset** to collapse the panel. ![Expanded Show what is saved into the preset panel with a connected_content Liquid template for a News Feeds preset](https://docs.pushwoosh.com/content-sms-presets-31.webp) ## Add MMS attachments On the same editor page, attach up to 3 images to turn the message into an MMS. Attachments are set per language, the same as the message text. 1. Scroll to **MMS attachments** — below **Content source**, or below the message fields for **Regular SMS**. 2. Optionally enter a **Subject**. A subject requires at least one image. A subject without an attachment is rejected. Up to 40 characters if the subject is plain ASCII, or 13 characters if it contains any non-ASCII character (for example Cyrillic, CJK, or most emoji). 3. Click **Add image** to pick a file from your project’s media library. You can attach up to 3 images. Use JPG or GIF files under 200 KB each — AbleMobile, the only MMS-capable provider, rejects the whole send otherwise. 4. Once you’ve added a second image, set **Show text after** to choose which image the message text should appear after (**Image 1**, **Image 2**, and so on). With a single image, the text is shown after it automatically. Note MMS is delivered only when your SMS provider supports it: currently [AbleMobile](https://ablemobile.com/). With any other provider, the subject and images are dropped and the plain-text message is sent instead. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/#supported-sms-providers) Leaving the attachments empty sends a regular SMS. There’s no separate MMS toggle. ## Send over RCS The **RCS** block sends a Twilio content template over RCS (Rich Communication Services) instead of the message text: a branded chat message with a card, carousel, media, or buttons. It needs Twilio connected with your own account credentials and a Messaging Service. See [RCS messages through Twilio](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) for the requirements and the Twilio setup. Each language of the preset can have its own template. A language without a template is sent as a regular SMS. 1. Switch to the language you want to set up, the same as in [Add languages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-languages). 2. Scroll to the **RCS** block. 3. In **Twilio template**, select a template. The list holds only templates of the connected Twilio account with an RCS content type: card, carousel, quick reply, call to action, or media. Each template is listed by name and language. To send this language as a regular SMS, leave the field at **Don’t send over RCS** or clear it. 4. If the template has variables, fill in a field for each one. ![RCS block with the Order shipped card template selected and the first template variable set to a FirstName tag](https://docs.pushwoosh.com/content-sms-presets-35.webp) 5. Keep the **Message** field filled in. A send without message text is rejected, even when the language has a template. ### Template variables A template can have variables, for example the first name in a card title. Each variable has its own field, titled by the variable, such as `{{1}}`. Type text, a [tag](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#personalize-sms-content), or Liquid in the field, the same as in the message text. A field left empty uses the template’s default value, shown as the field’s placeholder. Choosing a different template clears the variables you entered for the previous one. ### Update the buttons of a template A journey doesn’t see changes you make to a template’s quick reply buttons in Twilio until you save the preset again. To update the buttons: 1. Open the SMS preset. 2. Click **Save** at the top of the editor. Saving makes Pushwoosh read the buttons from Twilio and store a copy with the preset. Journeys build their [reply branches](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#branch-on-rcs-reply-buttons) from this copy. If a selected template no longer exists in Twilio, saving fails. Select another template in the **RCS** block and save again. ### What phones without RCS receive Phones that can’t receive RCS get an SMS built from the template’s **Text** type, not from the **Message** field. ![SMS preview with a note under the bubble that the message is sent over RCS and phones without RCS get the template's Text type](https://docs.pushwoosh.com/content-sms-presets-36.webp) ### RCS warnings The **RCS** block warns you when the template can’t be sent the way you expect. Each warning has its own fix: | Warning | What it means | What to do | | ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- | | **The template has no Text version, so phones without RCS won’t get the message** | The template has no SMS fallback. | Add the **Text** type to the template in Twilio, or pick another template. | | **Template … is no longer in the Twilio account, so the message will fail to send** | The template saved in the preset was deleted in Twilio. | Click **Remove template** and select another one. | | **Couldn’t load Twilio templates** | Pushwoosh couldn’t read the template list. Most often the application’s SMS settings have no Twilio **Account SID** and **Auth token** of your own, or Twilio rejected them. | Check the credentials in [SMS platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/). | ## Preview and save The preview panel on the right side of the editor shows how the message looks as an SMS bubble. It updates as you edit. 1. Check the preview on the right to see the message text and segment count. ![SMS content preview panel showing the message text and segment count](https://docs.pushwoosh.com/content-sms-presets-5.webp) 2. Click **Save** at the top of the editor. ![Save SMS content button in editor interface](https://docs.pushwoosh.com/content-sms-presets-7.webp) Once saved, use the preset in [SMS campaigns](https://docs.pushwoosh.com/product/messaging-channels/sms) and Customer Journeys. ## Manage SMS presets Saved presets appear under **Content > Messenger presets** on the **SMS** tab. The list shows each preset’s title, category, and last updated date. Use the search bars at the top to find content by name or category. From this page, you can edit, delete, or create new SMS content. ![SMS presets management interface showing list of created SMS content](https://docs.pushwoosh.com/content-sms-presets-8.webp) A preset with a **Rich message** badge is a copy of a [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/). You can’t edit or delete it or change its category here. Click it to open the rich message in its editor. If rich messages aren’t turned on for your account, the copy opens read-only in its own form. ### SMS categories Each SMS message has a category shown in the **Category** column. To change it, click the current category next to the SMS title, then: * **Assign a category:** select one from the dropdown, or create a new one. * **Remove the category:** select **Default**. Note [Content categories](https://docs.pushwoosh.com/product/content/#content-categories) apply to all types of content within a single project, except push. --- # Vouchers **URL:** https://docs.pushwoosh.com/product/content/vouchers/ Send unique promo codes with Pushwoosh voucher pools. Upload, manage, and insert vouchers into personalized messages for better engagement and conversion tracking. --- Voucher pools in Pushwoosh allow you to personalize push, email, WhatsApp, and other messages by inserting unique voucher codes. A voucher pool is a collection of codes that share common settings such as name and optional expiration date. ## Create a voucher pool To create a new voucher pool: 1. Go to **Content > Vouchers** in the Pushwoosh Control Panel. 2. Click **Create Voucher Pool**. ![Create voucher pool interface showing new voucher pool creation form in Pushwoosh dashboard](https://docs.pushwoosh.com/content-vouchers-1.webp) 3. Upload a `.csv` file containing your voucher codes in the window that opens. ![CSV file upload interface for voucher codes with file requirements and format specifications](https://docs.pushwoosh.com/content-vouchers-2.webp) ### CSV requirements * The file must have a column called `vouchers`. * The file size must not exceed **10 MB**. * If the file contains duplicate codes, only one copy of each will be added. * Save the file in `.csv` format and make sure it’s encoded in UTF-8. #### Example CSV file ```plaintext vouchers WELCOME2025-A1 WELCOME2025-B2 WELCOME2025-C3 WELCOME2025-D4 WELCOME2025-E5 ``` 4. Enter the name of the pool (max 300 characters). 5. Optionally, enable **Use expiry date** to define how long the voucher pool remains active. Set the validity period in GMT. Once the pool expires: * It will no longer appear in the list of available voucher pools in Customer Journey. * Vouchers from the expired pool will not be sent. * Any attempt to send a push message with them will result in a drop-off with the reason: **Voucher pool has expired**. ![Voucher pool configuration form showing name field and expiry date settings for promo codes](https://docs.pushwoosh.com/content-vouchers-3.webp) > **Note:** Creating a pool may take up to 10 seconds if the uploaded file contains a large number of vouchers. ## Add vouchers to an existing pool To add more vouchers to an existing pool: 1. On the voucher pool list page, click the three dots next to the target pool and select **Add vouchers**. ![Add vouchers to existing pool menu showing three dots options for voucher management](https://docs.pushwoosh.com/content-vouchers-4.webp) 2. Upload a `.csv` file that includes a **vouchers** column with the codes to add. 3. Click **Add vouchers**. Any duplicates already in the pool will be skipped. ![Add vouchers confirmation interface showing CSV upload and duplicate handling options](https://docs.pushwoosh.com/content-vouchers-5.webp) ## Delete vouchers To remove vouchers from a pool: 1. On the voucher pool list page, click the three dots next to the target pool and select **Delete vouchers**. 2. Upload a `.csv` file that includes a `vouchers` column with the codes to remove. Max file size is 10 MB. 3. On the **Confirm Action** step, click **Delete vouchers** to confirm, or **Cancel**. You can also click **Upload another file** to change the selection. ![Delete vouchers screen showing Upload CSV step with file must include vouchers column, drag and drop area, max 10 MB, and Cancel button](https://docs.pushwoosh.com/content-vouchers-10.webp) ## Delete pool To delete an entire voucher pool: 1. On the voucher pool list page, click the three dots next to the target pool and select **Delete pool**. 2. In the confirmation dialog, check the pool name and click **Delete** to confirm. Caution This action cannot be undone. ## Send vouchers via Customer Journey You can send unique voucher codes to users through various messaging channels in Pushwoosh Customer Journey, including [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), [email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/), [SMS](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/), [LINE](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/line/), [Data to app](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) and [WhatsApp](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/). This example shows how to send vouchers through push notifications using [Pushwoosh Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Follow these steps: 1. Create a push preset that includes the placeholder `{{voucher}}`. This can be placed in the title, subtitle, or message body. ![Push notification preset creation with voucher placeholder template for personalized promo codes](https://docs.pushwoosh.com/content-vouchers-6.webp) 2. In the Customer Journey, add a [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) and select a preset with vouchers. ![Customer Journey push element configuration showing voucher integration options for personalized campaigns](https://docs.pushwoosh.com/content-vouchers-7.webp) 3. Under **Overwrite personalization**, enable the **Use Vouchers** option. Select the voucher pool you want to use. 4. Optionally, set a [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). This tag is a custom attribute that will be saved on the user’s device when a voucher is sent. The exact voucher code delivered will be stored in this tag, allowing you to view it later in the [View Subscriber section](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#user-overview-tab) under the device or user profile. ![Voucher integration settings showing Use Vouchers toggle and pool selection for personalized promo codes](https://docs.pushwoosh.com/content-vouchers-9.webp) Tip Make sure to create the tag in advance. Give it a clear and descriptive name, set the **Value type** to **String**, and mark the **Tag type** as **User-specific**. This is required to store voucher values individually for each user. ![User tag creation interface for storing voucher codes with string value type and user-specific settings](https://docs.pushwoosh.com/content-vouchers-8.webp) ## Display a voucher as a barcode Instead of showing the voucher as plain text, show it as a scannable barcode or QR code. ### In email or in-app Use the built-in **Barcode** block. It builds the image for you: * [Email content blocks](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#barcode) * [In-app editor blocks](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#barcode) * [Native in-app messages](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-a-barcode) Cloud Pages has the same **Barcode** block. A page is one shared file for every visitor, so use it for a code that’s the same for everyone. For a voucher unique to each recipient, use email or in-app instead. ### In a push Works from a Customer Journey This works when the preset is sent by a Customer Journey [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) with **Use Vouchers** turned on and a pool selected (step 3 below). For a push notification, build the URL yourself and paste it into the preset: ```plaintext https://render.pushwoosh.com/barcode/v1/code.png?sym=code128&v={{voucher}}&hri=1&w=600 ``` 1. Copy the URL above. 2. In the [push preset](https://docs.pushwoosh.com/product/content/push-presets/#add-media-content), turn on **Media content** and paste the URL into **Banner URL**. Use **Banner URL**, not **Icon**: the icon renders too small to scan. ![Push preset Media content panel with the barcode URL pasted into the Banner URL field](https://docs.pushwoosh.com/content-presets-media-url-barcode.webp) 3. In the Customer Journey [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), turn on **Use Vouchers** and select a pool. See [Send vouchers via Customer Journey](https://docs.pushwoosh.com/product/content/vouchers/#send-vouchers-via-customer-journey). To change the barcode type, size, or whether digits print under the code, edit these parameters in the URL: * `sym`. Barcode type: * `qr`. A square 2D code phones can scan. Accepts any text, including alphanumeric codes like `WELCOME2025-A1` * `code128`. A linear barcode for any text, including the same alphanumeric codes * `ean13`. The common retail barcode in Europe and many other regions. Digits only, 12–13 characters * `ean8`. A shorter retail barcode for small packaging. Digits only, 7–8 characters * `upca`. The common retail barcode in North America. Digits only, 11–12 characters * `v`. The value to encode. Keep `{{voucher}}` (or a [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) tag) so it resolves to each recipient’s code at send time * `hri`. `1` prints the code under the barcode. `0` hides it * `w`. Width in pixels, from 32 to 2000. Use at least 600 so scanners can read it reliably * `h`. Height in pixels, from 32 to 2000 Caution Use `ean13`, `ean8`, or `upca` only for a voucher that’s digits only, at the length each expects (see the list above). For alphanumeric codes like `WELCOME2025-A1`, use `code128` or `qr` instead. Keep `v` non-empty. `{{voucher}}` in a Customer Journey Push element already covers this — the journey skips the send when the pool has no voucher left (see [Drop-off reasons](https://docs.pushwoosh.com/product/content/vouchers/#drop-off-reasons)). If you put a different tag in the URL yourself, set a default for that tag. ## Drop-off reasons Possible errors during message sending: | | | | ---------------------------- | ------------------------------------------------------------------------------------- | | No vouchers left in the pool | All vouchers in the selected pool have been used. Add more vouchers to continue. | | Voucher pool has expired | The pool’s expiration date has passed. Vouchers from this pool can no longer be sent. | --- # About Wallet pass builders **URL:** https://docs.pushwoosh.com/product/content/wallet-passes/ Design Apple Wallet and Google Wallet passes in the pass builder with a layout, branding, fields, a barcode, and a live preview. --- The pass builder is where you design a Wallet pass: pick a layout, add your branding, fields, and a barcode, and check a live preview before you generate the pass. Apple Wallet and Google Wallet each have their own builder, because the two platforms support different layouts and fields. Before you design a pass A developer connects the wallet platform to your application first. See [Getting started with Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/). [Apple Wallet pass builder ](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) [Google Wallet pass builder ](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) ## After you generate the pass * **Send it to users:** share the install link or QR code. See [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/). * **Edit and track it:** change the pass, delete it, or find its holders. See [Manage Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/). Tip To issue one pass design to many users and update shared fields on every issued pass at once, use [Wallet Pass Campaigns](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/) instead of building each pass separately. --- # Apple Wallet pass builder **URL:** https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/ Use the pass builder in the Control Panel to set style, branding, fields, barcode, and locations, then generate a signed .pkpass. --- The Apple Wallet pass builder is a visual designer in the Control Panel. You pick a pass style, fill in branding, fields, and a barcode, watch a live preview, and generate the pass, so you don’t need to write `pass.json` by hand. ## Create a pass In the Control Panel, go to **Campaigns → Wallet Passes**, open the **All passes** tab, select **Apple Wallet**, and click **Create pass**. ![All passes tab with Apple Wallet selected, the search and campaign filter, the Create pass button, and table columns for pass, style, user, campaign, and last update](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-1.webp) ## Configure the pass * **From scratch:** start with an empty pass. Choose a pass style, then set colors, images, fields, barcode, and other options yourself in the sections below. * **From a template:** click **Load template**, pick a built-in pass in **Select Template** (boarding pass, coupon, event ticket, generic, or store card). The template applies as soon as you click it. The form prefills pass style, colors, pass fields, barcode, and basic information, and sets **Relevant Date** to the current time. Templates don’t include images, so upload **Icon** and **Logo** before you generate the pass. Edit any value or change the style as needed. Pushwoosh replaces the template’s team and Pass Type IDs with your application’s values. ### Choose a pass style Under **PASS STYLE**, pick one of the five Apple Wallet pass styles. The style controls the layout and which field areas and images are available: * **Boarding Pass** * **Coupon** * **Event Ticket** * **Store Card** * **Generic** Pick the style before you add fields and images, because switching the style clears all pass fields. ### Add pass name and description Under **BASIC INFORMATION**, fill in: | | | | :-------------- | :----------------------------------------------------------------- | | **Pass Name** | The pass title, and the source name shown on update notifications. | | **Description** | A short description of the pass. | | **Logo Text** | Text shown next to the logo. | ![Basic information section with Pass Name, Description, and Logo Text fields](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-2.webp) ### Set colors and images #### Colors Under **COLORS**, set: * **Background Color:** the pass background. * **Foreground Color:** main text and icons on the pass. * **Label Color:** color of field labels (for example, `FROM`, `GATE`). To set or change a color, click its field and pick a color in the picker. Note Colors are stored in `rgb(r, g, b)` format. ![Colors section with Background Color, Foreground Color, and Label Color fields](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-3.webp) #### Images Under **IMAGES**, upload **Icon** and **Logo** (both required). Optional images depend on the pass style: | Image | Size | Available for | | :------------- | :------ | :------------------------------- | | **Icon** | 29×29 | All styles (required) | | **Logo** | 160×50 | All styles (required) | | **Strip** | 375×144 | Coupon, Event Ticket, Store Card | | **Background** | 180×220 | Event Ticket | | **Thumbnail** | 90×90 | Event Ticket, Generic | | **Footer** | 286×15 | Boarding Pass | Click **Choose File** next to each image slot and upload a PNG or JPEG file. Pushwoosh crops each image to the slot size from the center and prepares the resolutions Apple requires (@1x, @2x, @3x). ### Fill in pass fields Under **PASS FIELDS**, add the text and values that appear on the pass. Every style has the same field groups, with these limits: * **Header Fields (Top of pass):** up to 3 * **Primary Fields (Main content):** 1. On a Boarding Pass, the group is called **Primary Fields (Origin/Destination)** and holds 2 * **Secondary Fields (Supporting info):** up to 4 * **Auxiliary Fields (Extra details):** up to 4, or 5 on a Boarding Pass * **Back Fields (Flip side of pass)** Click **+ Add Field** in each group you need. For each field, set: | | | | :------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------- | | Key (ID) | Unique field identifier (generated automatically). | | Label | Short label on the pass (for example, `FROM`). | | Value | Value shown on the pass (for example, `SFO`). | | Change message | Text in the update notification when this value changes. Shown only when the value changes. Use `%@` for the new value (for example, `Gate changed to %@`). | ![Pass field with Format button and expanded formatting options for value format and text alignment](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-8.webp) On the field card, click **Format** to show formatting options. Click **X** to remove the field. #### Formatting options * **Value format:** Text, Date / time, or Number/currency. * **Text alignment:** Default (Natural), Left, Center, Right, or Natural. * **Data detectors** *(back fields only):* turn phone numbers, links, addresses, and calendar events into tappable content on back fields. ### Personalize pass content To show data unique to the recipient, such as a name or points balance, insert a [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) placeholder into a text field. The `{ }` button is available on: * **Pass Name**, **Description**, and **Logo Text** * A field’s **Label**, **Value**, and change message * **Barcode Message**, **Alt Text**, and **Grouping Identifier** To insert a placeholder: 1. Click `{ }` next to the text field. 2. Choose the tag. 3. Choose a **Format**. For the options, see [Personalize pass content](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#personalize-pass-content) in the Google Wallet builder, which uses the same dialog. 4. Optionally, enter a **Fallback (if empty)** value. The live preview shows the tag values of the user you enter in [User ID](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#enter-a-user-id). Without a User ID, it shows the placeholders. ### Add a barcode Under **BARCODE**, choose a format and enter the data to encode: 1. **Barcode Format:** choose how the barcode looks and what type of scanner can read it. * **QR Code (recommended):** square code that most phones and scanners read easily. Best for coupons, loyalty cards, and most passes. * **PDF417:** wide barcode made of several rows of lines. Often required for airline or transit boarding passes. * **Aztec:** compact square code, similar to QR. Common on event and transit tickets. * **Code128:** classic vertical-line barcode. Choose this only if your scanner or POS system requires a 1D barcode. 2. **Barcode Message:** the value encoded in the barcode (for example, a ticket or membership number). 3. **Alt Text (optional):** text shown below the barcode on the pass. ![BARCODE section with format dropdown, barcode message field, and optional alt text field](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-6.webp) ### Set expiration and status Under **EXPIRATION & STATUS**, control when the pass is shown, when it expires, and whether it is still valid: * **Relevant Date (when to show pass):** date and time when the pass may appear on the lock screen. * **Expiration Date (when pass expires):** date and time after which the pass is grayed out in Wallet. * **Mark as Voided (invalidate this pass):** checkbox to invalidate the pass so it is no longer usable. ![Expiration and status section with relevant date, expiration date, and voided checkbox](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-7.webp) ### Style-specific and advanced options * **Transit type** *(Boarding Pass only):* Air, Train, Bus, Boat, or Generic. * **Grouping identifier:** group related passes together in Wallet. ### Location-based relevance *(optional)* Under **LOCATION-BASED RELEVANCE**, the pass appears when the user is near the locations you add. For each location, set the text shown on the lock screen when the user is nearby. You can add up to 10 locations. Click the map, search an address, paste coordinates, or import a geozone to add a point. Drag the marker to fine-tune the position. A **max distance** (in meters) controls how close the user must be for the pass to stay relevant. ![Location-based relevance section with map, location list, lock screen text, and max distance field](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-5.webp) Locations vs. beacons The **LOCATION-BASED RELEVANCE** section only configures map-based locations. You define a place and **max distance** in meters. The device uses location services to decide when the user is close enough. Wallet can then show your lock screen text for that location. To tie a pass to iBeacon hardware indoors, use the **IBEACON RELEVANCE** section below. **Use case examples** * **Retail:** a loyalty card or coupon surfaces when the customer is close to your store. Lock screen text might say you are near the shop and remind them to use the pass. * **Events:** an event ticket appears when the user is near the venue (stadium, theater, or festival grounds) so they can find the pass quickly at the door. * **Travel:** a boarding pass becomes prominent near the departure airport or terminal, so the passenger sees gate or flight details on the lock screen before opening Wallet. * **Hospitality:** a coupon or membership pass can show up near a café or hotel location you define. ### iBeacon relevance *(optional)* Under **IBEACON RELEVANCE**, the pass appears when the device is near a physical iBeacon transmitter broadcasting a matching signal. This works indoors, where map-based location services are unreliable. To add a beacon, click **Add beacon** (up to 10 in total). To remove one, click **×** on its card. For each beacon, set: * **Proximity UUID:** the beacon’s identifier (for example, `B9407F30-F5F8-466E-AFF9-25556B57FE6D`). * **Major** and **Minor** *(optional)*: narrow the match to a venue and a single beacon. Each is a number from 0 to 65535, and 0 behaves as empty. Leave both empty, or set both to 0, to match any beacon broadcasting that UUID. * **Relevant text:** the text shown on the lock screen when the pass becomes relevant. ![iBeacon Relevance section with a beacon entry showing Proximity UUID, Major, Minor, and Relevant text fields](https://docs.pushwoosh.com/apple-wallet-passes-pass-builder-9.webp) ### Enter a User ID Enter the Pushwoosh [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/#how-are-users-and-devices-related) for the person who will receive this pass. Pushwoosh issues the pass to that user and lists it under **USER** on **All passes**. ### Preview and validate A live preview updates as you edit, so you can see roughly how the pass will look in Wallet. Preview may differ on a device The preview is an approximation. The pass may look different on a real device. The barcode shown is a placeholder. The real barcode is generated in the final `.pkpass`. To check the pass against Apple’s specifications without creating it, click **Validate pass**. The builder flags missing required images and other problems. ## Generate the pass Select **Generate**. Pushwoosh validates the pass and processes the images. Generation takes a few seconds, after which the pass appears in **All passes**. For editing (including update by serial number), downloading, and deleting passes after generation, see [Manage existing passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). To send the pass to users, see [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/). To issue one design to many users and update shared fields on every issued pass at once, create a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/) instead. A campaign uses the same design sections as this builder. ## Manage passes via API Everything the pass builder does is also available through the [PassKit Designer API](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/): create, update, list, download, and delete passes. [Apple Wallet PassKit API ](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) --- # Google Wallet pass builder **URL:** https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/ Design a Google Wallet pass with its type, branding, fields, and barcode, then share it with an Add to Google Wallet save link. --- The Google Wallet pass builder is a visual designer in the Control Panel. You pick a pass type, fill in branding, fields, and a barcode, watch a live preview, and create the pass. When you generate the pass, Pushwoosh creates the pass object in Google Wallet and returns an “Add to Google Wallet” save link. ## Create a pass Configuration required first The builder is available only after Google Wallet is configured for the application (Issuer ID, with the Pushwoosh service account invited to your issuer). If it isn’t configured yet, set it up first: [Google Wallet pass configuration for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/). In the Control Panel, go to **Campaigns → Wallet Passes**, open the **All passes** tab, select **Google Wallet**, and click **Create pass**. ![Wallet Passes page with the Google Wallet tab open and the Create pass button visible](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-1.webp) ## Configure the pass * **From scratch:** start with an empty pass. Choose a pass type, then set fields, appearance, a barcode, and other options yourself in the sections below. * **From a template:** click **Load template**, pick a built-in pass in **Select Template** (generic, offer, loyalty, event ticket, gift card, flight, or transit). The template applies as soon as you click it. The form prefills the pass type, fields, colors, and barcode, and sets a placeholder logo. Replace the logo with your own before you generate the pass, otherwise the placeholder ships on the pass. Edit any value or change the type as needed. ### Choose a pass type Under **PASS TYPE**, pick one of the seven Google Wallet pass types. The type controls the card layout and which fields are available. Typed passes use Google’s native layout. The generic pass is free-form. You can’t change the type after you create the pass. * **Generic:** free-form card from your own fields. * **Offer:** coupons and discounts. * **Loyalty card:** loyalty and membership cards. * **Event ticket:** tickets for events. * **Gift card:** gift and prepaid cards. * **Flight:** boarding passes. * **Transit:** rail, bus, tram, and ferry tickets. ### Fill in pass fields The fields you see depend on the pass type. The builder doesn’t mark required fields, but **Validate pass** and **Generate** report any that are empty. Common fields per type: | Pass type | Key fields | | :--------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Generic** | Card title*, Header*, Subheader, up to 6 fields on the card | | **Offer** | Title*, Provider*, Details, Fine print, Redemption channel (`BOTH`, `INSTORE`, `ONLINE`, or `TEMPORARY_PRICE_REDUCTION`), Issuer name | | **Loyalty** | Program name*, Member name, Member ID, Points balance, Points label, Rewards tier, Tier label, Issuer name | | **Event ticket** | Event name*, Venue, Address, Starts, Ends, Ticket holder, Ticket number, Ticket type, Section, Row, Seat, Gate | | **Gift card** | Merchant*, Card number*, PIN, Balance + currency | | **Flight** | Airline (IATA code)*, Flight number*, From*, To*, Departure*, Passenger name*, Boarding time, Seat, Class, Boarding group, Confirmation code | | **Transit** | Type* (bus / rail / tram / ferry / other), Operator* (unless Issuer name is set), Passenger*, Trip type, journey legs (origin*, destination*, times, platform, coach, seat), Issuer name | * Required by Google. Dates and times Most pass types use ISO 8601 with a UTC offset (for example, `2026-07-01T19:30:00+02:00`). Flight passes are an exception: Google requires departure, boarding, and arrival times in the airport’s local time without an offset (for example, `2026-09-01T06:30:00`). #### Personalize pass content Each pass is linked to one [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). Use tag placeholders to show data unique to that user, for example a points balance on a loyalty card or the recipient name on a gift card. Issue one pass per User ID so each recipient gets their own values. To insert a tag placeholder: 1. Next to a supported text field (for example, **Merchant**, **Card title**, or **Header**), click `{}` to insert a tag. 2. In **TAG**, select the [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) to insert into the field. 3. In **FORMAT**, choose how to display the tag value: **As is** (use the stored value), **Integer** (round to a whole number), **Price (0.00)** (show two decimal places), **Capitalize first letter**, **Date (5 Aug 1980)**, **Date and time (5 Aug 1980 14:30)**, or **Time (14:30)**. 4. In **Fallback (if empty)**, optionally enter text to show when the recipient has no value for the tag. 5. Click **Insert**. ![Pass field with the Insert a tag dialog open, showing Tag, Format, and Fallback options](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-2.webp) Pushwoosh fills in tag values when you create the pass and refreshes them on every update. ### Set appearance Under **APPEARANCE**, configure colors, images, who can save the pass, and expiration. #### Colors Click **Background Color** and pick a color in the color picker. #### Images Click **Choose an image** next to **Logo** to pick an image from the application’s [Media storage](https://docs.pushwoosh.com/product/content/media-store/), upload a new one, or get one from stock photos or AI generation. The picker accepts PNG and JPEG files. The logo is required for loyalty and transit passes. Optionally, click **Choose an image** next to **Hero image** for a wide banner shown below the card details on the front of the pass. Recommended size is 1032×336 px. ![APPEARANCE section with background color, Logo and Hero image pickers, Who can save the pass, and Expiration date fields](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-3.webp) If the application’s Media storage has a [crop](https://docs.pushwoosh.com/product/content/media-store/#create-image-crops) already sized for the placement, pick that crop. Deleting the image in Media storage Media storage blocks deleting an image only while a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/) uses it. An image picked for a single pass isn’t protected, so keep it in Media storage for as long as the pass is in use. #### Who can save the pass In **Who can save the pass**, select who can add the pass to Google Wallet. You can’t change this setting after the pass is created. * **One user, all their devices** (default): the pass is linked to one Google account and syncs across that account’s devices. * **One user, one device:** the pass is saved on a single device only. * **Anyone with the link:** every Google account that opens the save link gets the same shared pass object. When you update the pass, Google delivers the change to all holders automatically. Anyone with the link Use this for passes where all holders see the same content, for example a store-wide offer or a public event ticket with no assigned seat. Avoid it for loyalty cards, gift cards, or any pass where content must be personalized per user. Pushwoosh tracks only one device for the shared object, so individual holders are not visible and cannot be targeted separately. #### Expiration Set **Expiration date** to when Google should expire the pass, or leave it empty for no expiry. ### Add a barcode Under **BARCODE**, choose a format and enter the value to encode: 1. **Format:** choose how the barcode looks and what type of scanner can read it. * **QR_CODE:** square code that most phones and scanners read easily. Best for most passes. * **PDF_417:** wide barcode made of several rows of lines. Often required for boarding passes. * **AZTEC:** compact square code, similar to QR. Common on event and transit tickets. * **CODE_128** / **EAN_13:** 1D barcodes. Choose these only if your scanner or POS system requires them. 2. **Value:** the data encoded in the barcode (for example, a ticket or membership number). To use a unique value per user, click `{}` next to the field and insert a [tag placeholder](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#personalize-pass-content). 3. **Alt Text:** text shown below the barcode on the pass. ![BARCODE section with Format, Value, and Alt Text fields](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-5.webp) ### Add details fields and links On the front of the pass, users see the main card. When they open the pass details (the back), they can read extra sections and tap links you add here. * **Fields in details:** extra text on the back of the pass. Click **+ Add field** and set a **Label** and a **Value**. Use them for terms, instructions, or other information that does not fit on the front. * **Links:** tappable links on the back of the pass. Click **+ Add link**, then set a **URL** and a short **Link label** (for example, “Store locator” or “Terms and conditions”). ![Fields in details and Links sections with label, value, URL, and link label fields](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-6.webp) ### Add an app link *(optional)* Under **APP LINK**, add a button on the front of the pass that opens a web page or your Android app when tapped. You can set a **URL**, an **Android package name**, or both. If you set only the URL, the button opens that web address on any device. If you set only a package name, the button works on Android devices that have the app installed, with no web fallback. The URL must start with `https://`. After the pass is created, you can change the app link but not remove it. ![APP LINK section with URL and Android package name fields](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-4.webp) ### Add locations *(optional)* Under **Location-based relevance**, add points of interest. Google shows a geofenced notification when the user is near a location you add. For example, surface a loyalty card near your store, an event ticket near the venue, or a boarding pass near the airport. You can add up to **10** locations. To add one, click the map, search an address, paste coordinates, or import a geozone. ![LOCATIONS section with latitude and longitude fields for adding points of interest](https://docs.pushwoosh.com/google-wallet-passes-pass-builder-7.webp) Locations on Google Wallet Google controls the geofence radius and decides when to surface the notification. You cannot set a max distance per location. ### Enter a User ID Enter the Pushwoosh [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/#how-are-users-and-devices-related) for the person who will receive this pass. Pushwoosh issues the pass to that user and lists it under **USER** on [**All passes**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). ### Preview and validate A live preview updates as you edit, so you can see roughly how the pass will look in Google Wallet. Preview may differ on a device The preview is an approximation. Typed passes use Google’s native layout, so the pass may look different on a real device. A QR code in the preview encodes the real value, while other barcode formats are shown as placeholders. To check the pass against Google’s requirements without creating it, click **Validate pass**. The builder flags missing required fields and other problems. ## Generate the pass Click **Generate**. Pushwoosh checks the pass and creates it in Google Wallet. You get an **Add to Google Wallet** save link to share with users. Within a few seconds, the pass appears in [**All passes**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). For editing, getting the save link, invalidating, and deleting passes after creation, see [Manage existing passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). To send the pass to users, see [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/). To issue one design to many users and update shared fields on every issued pass at once, create a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/) instead. A campaign uses the same design sections as this builder. ## Manage passes via API Everything the pass builder does is also available through the [Google Wallet API](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/): create, update, list, get a save link, set state, and delete passes. [Google Wallet API ](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/) --- # Understanding web content **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/ Learn what web content and web forms are, how to choose between them, and where to build one and attach it to a web campaign. --- The **Web content** section holds the layouts visitors see when a web campaign appears on your website. Each item is one of two types: web content or a web form. Build either one under **Content → Web content**, then attach it to a [web campaign](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/). ## Choose web content or a web form Pick the type when you click **Create**. You can’t change it later, and a clone of the content keeps its type. * **Web content:** shows a message, for example an offer, an announcement, a product tour, or a rating request. See [Web content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/). * **Web form:** collects an email address and visitor answers, and saves the answers to tags. See [Web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). In the **Content → Web content** list, each item shows its type, **Web content** or **Web form**. To list only forms, set the type filter above the list from **All types** to **Web form**. ## Set up popup settings The **Popup settings** panel is available in both web content and web forms. Use it to set colors, dark theme, placement, and overlay. [Web popup settings ](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/)Colors, dark theme, placement, and overlay. ## Work with multiple pages Use more than one page to guide visitors through a short product tour or a multi-step offer, or to split a long form into steps. Multiple pages work in both web content and web forms. [Multiple pages ](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/)Add, duplicate, and delete pages. ## Start from a ready-made template The web content template gallery has ready-made web content for push opt-in, offers, lead capture, consent, surveys, and other use cases. Pick one, then replace the text and image with your own. [Web content template gallery ](https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/)Templates by category, with a suggested audience and timing for each. ## Use the content in a campaign Attach the saved content to a web campaign to show it on your site. [Create Web popup campaigns ](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/)Pick the content, set audience and display rules, then launch. ## Track campaign results Open the campaign’s statistics from **Campaigns → Web campaigns** to see how many times the campaign loaded on a page and was seen. For a web form, the statistics also show submits, failed submissions, and the subscribers you can export. [Web campaign statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/)Visits, impressions, submits, failed submissions, and the subscriber export. ## Delete web content To delete several web content items at once: 1. Go to **Content → Web content**. 2. Click **Bulk actions → Delete…**. 3. Select the content. 4. Click the **Delete** button above the list. Content used in web campaigns or journeys is skipped. Deletion can’t be undone. --- # Blocks **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/blocks/ Add and configure blocks in web content and web forms, including text, button, icon, countdown, rating, email input, and consent. --- Reference for every block you can add from the **Elements** tab in [web content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/) and in a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). ## How to configure blocks 1. Open the **Elements** tab on the right. ![Right panel Elements tab with tiles for Text, Button, Icon, Countdown, Topic picker, and Rating](https://docs.pushwoosh.com/content-web-popups-content-12.webp) 2. Drag a tile onto the popup, or click it to add it to the page. * A click inserts the block below the selection on the canvas. * If nothing is selected, the block goes at the end of the page’s first section. 3. Select a block on the popup to open the **Settings** tab (next to **Elements**). Adding a block from **Elements** opens **Settings** right away. To move a block anywhere on the popup, including into another section, select it and drag the handle that appears to its left into any gap. In **Settings** you can: * Configure that block’s own options * Move it up or down within its section with **POSITION** (up and down arrows) * Delete it with the delete icon Note When a section of the popup has no elements yet, it shows its own **+ Text** and **+ Button** controls in place. To add an Icon, Countdown, Topic picker, or Rating there instead: 1. Add an element using the **+ Text** or **+ Button** control. 2. Select it. 3. Add the block from the **Elements** tab. It lands in the same section, below your selection. ## Common blocks The **Text**, **Button**, and **Icon** blocks are available in both web content and web forms. ### Text Click any text or button label on the popup to edit it inline. A toolbar appears above it: * Apply **bold**, *italic*, or underline formatting. * Add or remove a link. * Set left, center, or right alignment. ![Selected text on the Web popup canvas with the inline editing toolbar showing formatting, alignment, and link controls](https://docs.pushwoosh.com/content-web-popups-content-5.webp) To change the text style: 1. Select the text and open the **Settings** tab on the right. 2. Choose a style: * **Icon:** a text style, not the **Icon** block. * **Eyebrow** * **Heading 1–3** * **Body** * **Caption** ![Text style dropdown in the Settings tab on the right, set to Eyebrow, with Position controls below it](https://docs.pushwoosh.com/content-web-popups-content-17.webp) ### Button Click a button to select it, then open the **Settings** tab on the right: * **Button style:** **Primary** or **Secondary**. * **Action:** * **Open a link:** enter the **Link URL**. **New tab** is on by default. Clear the checkbox to open the link in the same tab instead. * **Close the popup**. * **Subscribe:** request web push permission and subscribe the visitor. Only web content offers this action. A button in a web form has the other three. This is not the email **Subscription form** block. A granted request closes the popup. A denied one disables the button, because the browser won’t prompt twice. * **Go to page:** pick another page of the popup to switch to. This option appears once the popup has more than one page. See [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/). Use **POSITION** to move the button up or down, or the delete icon to remove it. ![Button Settings tab showing Button style Primary, Action Open a link, Link URL, and New tab checked](https://docs.pushwoosh.com/content-web-popups-content-8.webp) ### Icon Add an **Icon** block to drop a small pictogram (for example a percent sign for a sale, or a gift for a giveaway) next to your text. This is the Icon block, not the **Icon** text style under [Text](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#text). 1. Open the **Elements** tab on the right. 2. Click **Icon**. 3. Set the options in the **Settings** tab: * **Icon:** pick from a fixed set of 20 icons (bell, gift, cart, chart, lock, calendar, check, star, clock, tag, mail, phone, download, play, shield, sparkle, percent, truck, ticket, user). Custom icon uploads aren’t supported. * **Size:** 24, 32, or 48 px. * **Accent:** on by default, so the icon matches the popup’s accent color. Turn it off to pick a custom color. ![Icon block on the Web popup canvas next to text, with Icon Settings showing icon picker, Size, and Accent](https://docs.pushwoosh.com/content-web-popups-content-23.webp) ## Web content blocks The **Countdown**, **Topic picker**, and **Rating** blocks are only in web content. A web form doesn’t have them. Some templates in the [Web content template gallery](https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/) already include these blocks. You can’t add a **Subscription form** block to new content, but web content that already has one can still edit it. See [Subscription form](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#subscription-form). ### Countdown Add a **Countdown** block to build urgency with a deadline that ticks down live on the popup. 1. Open the **Elements** tab on the right. 2. Click **Countdown**. 3. Set the options in the **Settings** tab: * **Deadline:** the date and time the countdown reaches zero. * **Time zone:** the zone the deadline is read in, not the visitor’s own time zone. Defaults to your browser’s time zone; pick **UTC** from the list to read the deadline literally instead. * **Language:** the language of the unit labels (days, hours, and so on). Leave it on **English (default)** or pick from 14 other languages. * **Labels:** turn on to show unit labels next to the numbers. * **When the deadline passes:** * **Show zeros:** the countdown keeps showing 00:00:00. * **Do not show the popup:** the whole popup is skipped once the deadline is in the past. Pushwoosh checks the deadline right before the popup would appear. A popup skipped this way doesn’t count against **Once per visitor** frequency. * **Go to page:** jump to another page of the popup when the deadline passes. Only offered once the popup has more than one page. Pick the target page from the dropdown that appears. See [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/). ![Flash sale Web popup with a Countdown block showing days, hours, minutes, and seconds under a Shop now button](https://docs.pushwoosh.com/content-web-popups-content-18.webp) ### Topic picker Add a **Topic picker** block to let visitors self-select interests as checkboxes or chips. Their picks are written to a [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) on their device. 1. Open the **Elements** tab on the right. 2. Click **Topic picker**. The block appears with two placeholder options. Its settings open in the **Settings** tab. ![Topic picker block on the Web popup canvas with interest options as chips or checkboxes and Settings open on the right](https://docs.pushwoosh.com/content-web-popups-content-19.webp) 3. In **List tag the choice is written to**, pick an existing List-type tag, or choose **Create a list tag** to make one without leaving the editor. Until you pick a tag, the block does not write anything. 4. Set **Control** to **Checkboxes** or **Chips**. 5. Set **Minimum picks** and **Maximum picks**. `0` on either end means no limit on that side (`0 = any` in the field labels). At least one pick is always required to submit, even when Minimum is `0`. Picking past the maximum, or trying to submit under the minimum, shows an inline message next to the block instead of sending anything. ![Topic picker settings with the Subscribed Email Categories list tag, Chips control, and Minimum and Maximum picks](https://docs.pushwoosh.com/content-web-popups-content-25.webp) 6. Under **Options and copy**, edit each option’s label (what the visitor reads) and **Tag value** (what’s written to the tag). If you leave **Tag value** empty, Pushwoosh writes the option’s internal ID instead (`option-1`, `option-2`, and so on). Use **+ Option** to add another row, up to 12 options total. ![Options and copy list with four options and their tag values, and the + Option button](https://docs.pushwoosh.com/content-web-popups-content-27.webp) 7. Set the **Button label** (default **Save**) and the **Message after sending** (default **Thanks! Your choice is saved.**). ![Topic picker settings with Button label Save my topics, Message after sending, and the Position controls](https://docs.pushwoosh.com/content-web-popups-content-26.webp) Picks only add, they never remove A visitor’s picks are appended to the tag. Submitting the block again adds more values. It never clears previous ones. A popup can’t let a visitor un-pick a topic they chose before. That needs a different surface, like a preference page backed by the same tag. ### Rating Add a **Rating** block to collect a quick score (NPS, stars, or thumbs) as a custom event. 1. Open the **Elements** tab on the right. 2. Click **Rating**. The block appears with no event set. Its settings open in the **Settings** tab. ![Web popup with a Rating block using a five-star scale, Send rating button, and Next and Skip actions](https://docs.pushwoosh.com/content-web-popups-content-20.webp) 3. Set **Scale** to **NPS 0–10**, **Stars** (1 to 5), or **Thumbs** (up or down). Across all three, a higher score means a more positive rating. Thumbs sends `1` for a thumbs-up and `0` for a thumbs-down. 4. Set **When the score is sent** to **On tap** (sends when the visitor picks a value) or **With a button** (waits for a submit click). 5. In **Event the score is sent as**, pick an [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) from your project, or choose **Create an event** to define a new one without leaving the popup. The score is stored as that event (`score`, `scale`, and `slotKey` attributes). Use it in segments or Customer Journeys like any other event. Until you pick an event, the block does not send anything. 6. Optionally fill **Left label** and **Right label** at the two ends of the scale. 7. If **When the score is sent** is **With a button**, set the **Button label** (default **Send rating**). ![Rating Settings tab showing Scale, When the score is sent, Event, Left and Right labels, Button label, and Position controls](https://docs.pushwoosh.com/content-web-popups-content-21.webp) Creating an event here is permanent * An event you create from this menu is a real event on your project, not a draft. Deleting it later means going to your project’s event list, not just changing the popup. * If an event bound to the block is later renamed or deleted elsewhere, or was typed into this field before it became a picker, the picker shows **Missing: **. It does not silently drop or swap the event. Pick a different event to fix the binding. ### Subscription form The **Subscription form** block links the popup to a [subscription form](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) from **Audience → Subscription forms**. * The block isn’t offered in **Elements**, so you can edit it only in web content that already has it. * To collect emails and other fields in new content, create a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) instead. * For web push permission, use a button with the **Subscribe** action. To edit the block, select it on the popup. Its settings open in the **Settings** tab. ![Web popup on the canvas with a Subscription form block showing an Email field and Subscribe button](https://docs.pushwoosh.com/content-web-popups-content-24.webp) #### Choose a form source With the block selected, the **Settings** tab shows a **Subscription form** dropdown that links the block to a [subscription form](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) in your project. You can create a new form or pick one that already exists: | Option | When to use | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **New form — created on save** (default) | Fastest start: Pushwoosh creates the form for you when you save the popup, named “*popup name* — web popup”. | | **+ Create new form…** | You want to name the form and set it up before you save the popup. Opens a **New subscription form** dialog. Enter a **Name**, set **Fields**, **Submit** text, and **Privacy**, then click **Create**. | | An existing form from the dropdown | Reuse a form that already has subscribers or double opt-in configured. | Important If you pick an **existing** form from the dropdown, saving this popup updates that subscription form for the whole project. The fields, submit button text, and success messages you set in this block replace what that form had before. That includes the form embedded on your website and the form in **Audience → Subscription forms**. Other Web popups that use the same form are not updated. Each popup keeps its own copy of the block’s fields and texts until you reopen that popup and save it again. #### Set fields and submit behavior Still in the **Settings** tab: 1. Set the **Submit button style**: * **Primary:** filled button in the popup **Accent** color. Best when **Subscribe** is the main action on the page. * **Secondary:** outlined button with a border and accent-colored label. Best when the popup already has a primary button or you want a lighter look. 2. Turn on **Privacy** to add a privacy-consent line under the form, then enter your privacy policy URL in the field that appears. ![Privacy toggle enabled in the Settings tab with a privacy policy URL field](https://docs.pushwoosh.com/content-web-popups-content-13.webp) 3. Under **Fields**, keep **Email** (always required) and add **First name**, **Last name**, **Phone**, or **Custom**. * For a **Custom** field, pick the [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) its value submits to from the **Pick a tag…** dropdown, or choose **Create a tag** to make a new string tag without leaving the editor. * Set the **Success** and **Confirm email** texts shown after a visitor submits. ![Fields section in the Settings tab with Email, a Custom field bound to the Acquisition Campaign tag, add-field buttons, and the Success and Confirm email texts](https://docs.pushwoosh.com/content-web-popups-content-15.webp) ## Web form blocks The **Email input**, **Tag input**, **Consent**, and **Submit** blocks are only in web forms. See [Web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) for how a form works and how to save it. Note The texts of a block, including its error messages, are shown to visitors exactly as you enter them. Write them in the language of your site. ### Email input The field where the visitor enters an email address. It is always required. 1. Set **Label** and **Placeholder**: the field caption and the hint inside the empty field. Defaults are **Email** and `you@example.com`. 2. Edit **Invalid email text**, shown when the address is malformed or Pushwoosh rejects it. Default is **Enter a valid email**. 3. Edit **Required field text**, shown when the field is empty. Default is **Required**. ![Email input settings with Label, Placeholder, Invalid email text, and Required field text, and the Position controls below](https://docs.pushwoosh.com/content-web-forms-8.webp) A form with an **Email input** also needs a [Consent](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#consent) block with a privacy policy URL. ### Tag input A text field whose answer is saved to a [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) on the visitor’s device. The form editor can’t create tags, so create the tag in **Audience → Tags** before you add the field. 1. In **Tag**, choose an existing tag. The list shows String tags only, and you can type to search. Each tag can be used in one field of the form. 2. Set **Label** and **Placeholder**. 3. Select **Required** if the visitor must fill the field, and edit **Required field text** if needed. ![Tag input settings with the City tag chosen, City label, Where do you live? placeholder, and Required selected](https://docs.pushwoosh.com/content-web-forms-4.webp) A form can’t write tags whose names start with `PW_FF_`. ### Consent A checkbox with a link to your privacy policy. When the form has several consent checkboxes, the visitor must select all of them to submit. 1. Set **Checkbox text** and **Link text**: the text before the link and the link itself. Defaults are **I agree to the** and **privacy policy**. 2. In **Privacy policy URL**, enter the page the link opens: a full `https://` address or a path on your site that starts with `/`. ![Web form on the canvas with City, Email, consent checkbox, and Submit, and the Consent settings with a privacy policy URL](https://docs.pushwoosh.com/content-web-forms-5.webp) 3. Edit **Consent required text**, shown when the visitor submits without selecting the checkbox. Default is **Please accept the privacy policy**. ### Submit The button that sends the form. 1. In **Button style**, choose **Primary** or **Secondary**. 2. Edit **Button text**. Default is **Submit**. 3. Edit **Submit error text**, shown when the submission fails, for example because of a network error. The visitor can submit again. Default is **Something went wrong. Please try again.** ![Submit settings with Button style Primary, Button text Submit, and Submit error text, and the Position controls below](https://docs.pushwoosh.com/content-web-forms-9.webp) --- # Multiple pages **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/ Add, duplicate, and delete pages in a Web popup content layout, and set which page visitors see first when the popup opens. --- [Web content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/) and [web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) can have more than one page, for example to guide visitors through a short multi-step offer or to split a long form into steps. Each page has its own layout, media, and size. See [Popup settings](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/) to style and size a page. ## Add a page Click **+ Add page** to open the add-page menu: * **Duplicate current page:** copies the active page, including any edits you’ve made, as a new page. If the source page has a subscription form, the copy is created without it, because a popup can only have one. In a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/), the copy has no form blocks. * Pick one of the single-page layouts to add a fresh page with that layout’s demo content. The new page is independent of the others, so it can use a different layout. Note Multi-page example layouts, like **Product tour (multi-page)**, always come as a full set of pages. They’re only available from the starting gallery, not from this menu. See [Web content template gallery](https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/) for the templates that have more than one page. A new page is added after the current page. In a web form, it never goes after the **Success** page, which always stays last. Each tab is one page. Click a tab to switch to it. ## Set the start page The tab marked with ★ is the **start page**, the page visitors see first. To change it, open another tab and click ☆ on that tab. In a web form, the **Success** page can’t be the start page. ![Page tabs showing start-page star marker on one tab and delete control on the active tab](https://docs.pushwoosh.com/content-web-popups-content-7.webp) ## Delete a page On the active tab, click **✕**. A confirmation dialog opens. Click **Delete** to remove the page. This can’t be undone. In a web form, you can’t delete the **Success** page. Note Any button or countdown with a **Go to page** action pointing to the deleted page falls back to the **start page**. --- # Web popup settings **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/ Style and place Web popup content, including media and video, size, colors, dark theme, and placement. --- Use the **Popup settings** panel to set how a popup looks and where it appears. The panel is available in both [web content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/) and [web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). The panel has two parts: * **General settings:** the look and placement of the whole popup. * **Current page:** media and size for the page you’re editing. Page tabs and **+ Add page** sit at the top of the panel. See [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/). Note These settings control the popup design only. Audience, page targeting, delay, frequency, and launch settings are configured in the [Web popup campaign](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#start-a-new-campaign). ## Images and video Use these settings to choose the images and video of the popup, and to fit and soften them. If the layout includes one or more media blocks, open **Current page**. Each media block has a preview and controls. 1. Click **Change image** to pick a file from your project’s [Media storage](https://docs.pushwoosh.com/product/content/media-store/), or paste or edit the **Image URL** directly. 2. Set how the image fills its frame with **Fit** (**Natural**, **Cover**, or **Contain**). **Fit** is not shown for a background image, which always covers its area. 3. Optionally soften the image with **Blur** (0 to 20 px). 4. For background media only, use **Dim** (0 to 100%) to add a dark scrim so text on top of the image stays readable. ![Popup settings with a filled image preview, Change image button, and Fit and Blur controls](https://docs.pushwoosh.com/content-web-popups-content-2.webp) ### Dark theme image A popup with dark theme can show a different image in dark mode, for example a logo with dark ink. With [dark theme](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode) enabled, each image block adds a **Dark theme image** row. 1. Click **Change image** or set **Image URL (dark theme)** to pick a different image for dark mode (for example a logo with dark ink). 2. Leave it empty to keep showing the light **Image URL** in both schemes. The dark theme image applies everywhere that block’s image paints, including a background image or a video poster. ![Dark theme image settings with Change image and an empty Image URL (dark theme) field, below the light Image URL field](https://docs.pushwoosh.com/content-web-popups-content-dark-image.webp) ### Video Use a video instead of an image in a media block. 1. Set **Media** to **Video**. 2. Enter a **Video URL**: a direct link to the video file. Video only accepts an external URL. You cannot upload a video to the Media storage. Export it as an MP4 (H.264 video, AAC audio) with “fast start” enabled (sometimes called “web-optimized”), so the browser can start playing before the whole file downloads. If the URL fails to load, visitors just see the poster. 3. **Loop** is on by default, so the video replays continuously. Turn it off to stop after one play. 4. Turn on **Show controls** to display the browser’s built-in play, pause, and volume controls. Videos always start muted. Without this option, visitors have no way to turn the sound on. 5. Optionally set a **Poster URL**: the image shown while the video loads. Recommended. On an older popup script that doesn’t support video, the poster is the only thing shown. Leave it empty, and that visitor sees nothing where the video would be. ![Popup settings panel with Media set to Video, showing the Video URL, Loop, Show controls, and Poster URL fields](https://docs.pushwoosh.com/content-web-popups-content-video-1.webp) ### Media layout Some layouts add a **Template variables** section with placement settings for the media block. The section only appears when the current layout has settings like these. When the layout has media beside the text, set: * **Image width:** the share of the popup width, as a percentage. * **Image position:** **Left** or **Right**. On a bottom bar layout, the section offers **Buttons width** instead of image controls. ## Popup size and style Set the size of the popup, the rounding of its corners, its shadow, and how it appears. 1. Under **Current page** → **SIZE**, leave **Width** and **Height** empty for a popup that sizes itself to its content, or set a fixed size in pixels. In a popup with multiple pages, each page has its own size. 2. Turn on **Full height** when the page should fill the available height. 3. In **General settings**, set **Corner radius** and **Shadow** for the popup’s corner rounding and drop shadow. 4. Choose **Animation**: how the popup appears (**Default**, **None**, **Fade**, or **Slide**). ![Popup section in the settings panel with width, height, corner radius, shadow, and animation controls](https://docs.pushwoosh.com/content-web-popups-content-3.webp) With [dark theme](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode) enabled, **Shadow** follows the sun/moon scheme switch. A shadow tuned for a light page is often invisible on a dark one, so the two usually need different values. To edit the dark-palette shadow separately from the light one: 1. Set the sun/moon switch to **Dark**. 2. Edit **Shadow**. If you leave the dark shadow untouched, the light **Shadow** value is used in both schemes. ## Colors Set the colors of the popup: its background, the accent of its buttons, and its text. 1. Click **Colors** to expand the popup’s color palette. 2. Set **Background** and **Accent (buttons)** for the popup background and the accent color applied to buttons. 3. Set **Text** and **Secondary text** for body text and for secondary text such as captions and eyebrows. 4. If the layout has text over a background image, set **Text over image** for that text. A web form doesn’t have this color. ![Colors section expanded, showing Background, Accent, Text, and Secondary text pickers](https://docs.pushwoosh.com/content-web-popups-content-11.webp) ## Dark mode Dark mode gives the popup a second palette for visitors whose device is set to a dark color scheme. 1. In **Colors**, turn on **Enable dark theme**. With it off, the popup always renders with the light palette above, on every visitor. 2. Use the sun/moon switch above the color fields to pick which palette you’re editing. The canvas redraws in that scheme. 3. Click **Auto from light** to (re)generate the dark palette from the current light colors. Backgrounds and surfaces turn dark, and text turns light. **Accent** and **Text over image** carry over unchanged. Edit any of the five fields afterward to fine-tune. ![Colors section with Enable dark theme checked, showing the sun/moon scheme switch, Auto from light button, and the canvas rendering the popup with the dark palette](https://docs.pushwoosh.com/content-web-popups-content-dark-mode.webp) Turning **Enable dark theme** back off keeps the dark palette. Switching it on again restores your edits instead of regenerating from scratch. The same sun/moon switch appears next to **Close preview (Esc)** when you open **Preview** on a popup with dark theme enabled. You can check both palettes in the full-size preview too. ![Full-size preview with the sun/moon scheme switch next to Close preview, and the popup rendered in the dark palette](https://docs.pushwoosh.com/content-web-popups-content-dark-mode-preview-toggle.webp) A visitor’s browser decides which palette they see, based on their OS-level dark mode setting. There’s no dark/light toggle on the popup itself. A site can force one scheme for everyone regardless of OS setting. See [webPopups.colorScheme](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#web-popups) in the Web SDK docs. Dark theme also affects [images](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#images), [shadow](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#popup-size-and-style), and [overlay](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#placement-and-overlay) in this panel. Each follows the same sun/moon scheme switch once enabled. ## Placement and overlay Choose where the popup appears on the page and whether the page behind it is dimmed. 1. In **PLACEMENT**, pick where the popup anchors on the page using the **Position** 3×3 grid. 2. Set **Edge offset** to the distance in pixels from the selected edge. 3. In **OVERLAY**, turn on **Dim the page behind** to darken the rest of the page while the popup is shown, in both schemes. Turning it off removes the dim entirely, including any dark-scheme values set below. 4. Set **Color** and **Opacity** for the overlay (opacity from 0 to 1). ![Web popup settings panel showing PLACEMENT grid and OVERLAY dim controls](https://docs.pushwoosh.com/content-web-popups-content-4.webp) With [dark theme](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode) enabled, **Color** and **Opacity** also follow the sun/moon scheme switch. Set the switch to **Dark** to give the dim its own color and strength for the dark palette. Until you edit them there, the dark scheme shows the same **Color** and **Opacity** as the light scheme. * Dark ![Overlay and Style sections with the scheme switch on Dark, showing a custom purple overlay color and a matching glow shadow](https://docs.pushwoosh.com/content-web-popups-content-dark-overlay-dark.webp) * Light ![Same sections on Light, showing the original overlay color and shadow, unaffected by the dark-scheme edit](https://docs.pushwoosh.com/content-web-popups-content-dark-overlay-light.webp) --- # Web content **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/web-content/ Build web content from a starting template, edit blocks and pages in the editor, then save it and attach it to a web campaign. --- Use web content to show a message on your site, for example an offer, an announcement, or a product tour. Pick a starting template, edit it in the editor, and attach it to a [web campaign](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/). To collect emails or visitor answers, create a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) instead. ## Start from a template 1. Go to **Content → Web content**. 2. Click **Create** and choose the **Web content** type. 3. Pick a starting template from the gallery. * Use the category dropdown in the top right to filter by use case (for example **Promotions & offers**, **Gaming & marketplaces**, or **Feedback & surveys**), or leave it on **All templates**. * To start from a minimal layout, open **Blank layouts**. * Each category and template is described in [Web content template gallery](https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/). ![Web popup template gallery with a category filter, showing modal, image, bottom bar, side panel, and multi-page product tour starting templates with real illustrations](https://docs.pushwoosh.com/content-web-popups-content-1.webp) 4. [Build the content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/#build-the-content) in the editor. ### Name the content Click the content name in the top bar to edit it. The name identifies this content inside Pushwoosh, in the content list and the campaign wizard. Website visitors do not see it. ## Build the content Undo and redo * **Cmd/Ctrl+Z:** undo. * **Cmd/Ctrl+Shift+Z:** redo (Windows/Linux also accept Ctrl+Y). Cmd+Y is reserved by the browser, so redo on Mac only responds to Cmd+Shift+Z. While a text field is focused, the shortcuts undo or redo changes in that field instead. 1. Open the **Elements** tab on the right. Drag a block onto the content, or click it to add it to the page. See [Blocks](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/) for every block. 2. Select a block on the content. Set its options, position, or delete it in the **Settings** tab. 3. To change how the whole content looks and sits on the page, use **Popup settings** on the left: media, size, colors, and [dark theme](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode). See [Web popup settings](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/). 4. To add more pages, see [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/). ## Save the content 1. Open **Preview** to check the content at full size. 2. If dark theme is on, use the sun/moon switch next to **Close preview** to check both palettes. See [Dark mode](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode). 3. Check the images, video, and button actions. 4. Use **Close preview (Esc)** to return to the editor. 5. Click **Save** in the top bar. Note Saving content does not show it on your site. The content appears on the site only after you select it in a web campaign and launch the campaign. --- # Web content template gallery **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/ Pick a ready-made web popup from the web content template gallery, grouped by use case, and adapt its text and image to your site. --- The web content template gallery has ready-made popups grouped by use case: push opt-in prompts, offers, lead capture, consent gates, surveys, and more. Pick a template, then replace its text and image with your own in the [editor](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/#build-the-content). The gallery has no web form templates. To collect emails or visitor answers, create a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) instead. ## Open the gallery You open the gallery when you create new web content. 1. Go to **Content → Web content**. 2. Click **Create** and choose the **Web content** type. 3. To see one category, choose it in the dropdown in the top right. Leave it on **All templates** to see every template. The sections below list the templates by the same categories. Suggested audience and timing Each category except **Blank layouts** has a suggested audience and timing. The suggestions come after the list of templates. These are recommendations, not settings, because a template has no targeting of its own. You choose who sees the content and when in the [web campaign](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#define-audience). ## Before you save the content Check these four things in the editor before you save content made from a template: * Replace the template images with your own. Templates with a background image ship with a dark placeholder image. * Set a link for each button that should open a page. Template buttons that don’t close the popup or switch pages come without a link. * A template with a **Topic picker** or **Rating** block doesn’t write anything until you pick a tag or an event for it. See [Topic picker](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#topic-picker) and [Rating](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#rating). * A template with a **Countdown** block comes with a placeholder deadline at midnight a week ahead. Set your own **Deadline** in the block settings. See [Countdown](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#countdown). Then save the content and attach it to a web campaign. See [Save the content](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/#save-the-content). ## Blank layouts Start from a layout with placeholder content when no use-case template fits. ![Web content gallery filtered to Blank layouts, showing eight layout tiles from a sale modal to a full-height side panel](https://docs.pushwoosh.com/content-web-content-templates-1.webp) **Product tour (multi-page)** comes with three product pages and a **Next product** button that steps to the next one. See [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/). ## Push opt-in Ask visitors to turn on browser push notifications. ![Web content gallery filtered to Push opt-in, showing five opt-in templates including a two-step consent and a minimal bar](https://docs.pushwoosh.com/content-web-content-templates-2.webp) * **Breaking news alerts:** promise alerts only for big stories. * **Two-step consent:** ask about interest first, then explain the browser prompt on the second page. * **Minimal opt-in bar:** the same ask as a small bar instead of a popup. * **Notifications are off:** tell a visitor who blocked notifications how to allow them again in the browser. Show it to visitors who blocked notifications earlier. * **Product updates:** offer short notes on new features. **Suggested audience:** visitors who haven’t subscribed to push notifications yet. **Suggested timing:** after the visitor has seen some content, for example on their second page view, not the moment the site opens. ## News & media Bring readers back to a story or turn them into subscribers. ![Web content gallery filtered to News and media, showing Continue reading, Subscriber-only story, Weekly digest, and Special report](https://docs.pushwoosh.com/content-web-content-templates-3.webp) * **Continue reading:** invite a reader back to the story they left halfway. * **Subscriber-only story:** tell a reader the story is for subscribers and point to subscription plans. * **Weekly digest:** offer a weekly editors’ pick. * **Special report:** promote a long read. **Suggested audience:** returning readers, readers of a specific section, or readers who aren’t subscribers yet. **Suggested timing:** when a reader returns to an unfinished article, hits subscriber-only content, or finishes reading a story. ## Promotions & offers Drive a purchase with a discount, a bonus, or a deadline. ![Web content gallery filtered to Promotions and offers, showing seven templates from a welcome discount to a flash sale countdown](https://docs.pushwoosh.com/content-web-content-templates-4.webp) * **Welcome discount:** offer a discount on the first order. * **Back in stock:** offer to notify a shopper when a sold-out item returns. * **Free shipping threshold:** tell a shopper they’re close to free delivery. * **Promo code:** show a code to use at checkout. * **In-game bonus:** offer a free starter pack. * **Welcome bonus:** a first-deposit bonus with a terms footnote. * **Flash sale countdown:** a flash sale offer with a live [Countdown](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#countdown) block. **Suggested audience:** first-time visitors for welcome offers, shoppers with items in the cart, or players and shoppers you want to reactivate. **Suggested timing:** on entry for a welcome offer, on product and cart pages for stock and shipping messages, and during the sale window for time-limited deals. ## Gaming & marketplaces Onboard players and buyers, and announce new item releases and matches. ![Web content gallery filtered to Gaming and marketplaces, showing Welcome quest, Item drop, Match alerts, and Drop countdown](https://docs.pushwoosh.com/content-web-content-templates-5.webp) * **Welcome quest (3 steps):** a three-page onboarding. * **Item drop:** announce newly released items. * **Match alerts:** offer alerts when a match starts, when a team scores, and when the match ends. * **Drop countdown:** count down to a limited-time item release with a [Countdown](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#countdown) block. **Suggested audience:** new players or buyers, fans of a specific team or game, or users who follow specific items. **Suggested timing:** on the first visit for onboarding, and ahead of an item release or a match for alerts. ## Finance & trading Offer market alerts and analyst content with the disclaimers your audience expects. ![Web content gallery filtered to Finance and trading, showing Price alert, Trading signals, Opt-in with risk disclaimer, and Market open reminder](https://docs.pushwoosh.com/content-web-content-templates-6.webp) * **Price alert:** offer an alert when an instrument crosses a price level. * **Trading signals:** offer a daily note from your analysts. * **Opt-in with risk disclaimer:** an alerts opt-in with a risk warning in the footnote. * **Market open reminder:** offer a reminder before each session. **Suggested audience:** registered traders, users who follow specific instruments, or visitors who haven’t turned on alerts yet. **Suggested timing:** while the visitor views an instrument or a market page, or ahead of a trading session. ## Lead generation Turn visitors into leads with an event, a report, a demo, or a short question. ![Web content gallery filtered to Lead generation, showing five templates including Webinar registration and Pick your topics](https://docs.pushwoosh.com/content-web-content-templates-7.webp) * **Webinar registration:** invite visitors to a live webinar. * **Report download:** offer a free report. * **Book a demo:** offer a walkthrough, with a second button for visitors who only want the highlights. * **One-question survey:** ask whether the visitor found what they were looking for. * **Pick your topics:** let visitors choose topics with a [Topic picker](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#topic-picker) block. **Suggested audience:** visitors who aren’t customers yet, or visitors reading content about a specific topic. **Suggested timing:** after the visitor has read a page or scrolled through most of an article. ## Consent & gates Ask for consent or a choice before the visitor continues. ![Web content gallery filtered to Consent and gates, showing Cookie consent, Age gate, Region and language, and Get the app](https://docs.pushwoosh.com/content-web-content-templates-8.webp) * **Cookie consent:** tell visitors the site uses cookies. * **Age gate (18+):** ask visitors to confirm they’re of legal age. * **Region / language:** offer to switch to the visitor’s regional site. * **Get the app:** invite web visitors to install your app. **Suggested audience:** all visitors for cookie consent and age gates, visitors from another region for the region switch, and mobile visitors for the app prompt. **Suggested timing:** on the first page view. ## Feedback & surveys Collect a score or feedback with a [Rating](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#rating) block. ![Web content gallery filtered to Feedback and surveys, showing NPS survey, Rate your experience, and Rating, then topics](https://docs.pushwoosh.com/content-web-content-templates-9.webp) * **NPS survey:** ask how likely the visitor is to recommend you on a 0–10 scale. * **Rate your experience:** ask for a rating from one to five stars. * **Rating, then topics:** a star rating on the first page and a **Topic picker** on the second. **Suggested audience:** active users or customers who have just completed a purchase or another key action. **Suggested timing:** right after the key action, or after the visitor has used the site for a while. --- # Web forms **URL:** https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/ Build a web form that collects emails and saves visitor answers to tags, then show it in a Popup or Inline web campaign. --- A **web form** is web content with input fields: an email field, fields that save answers to tags, a consent checkbox, and a submit button. Show it in a Popup or Inline [web campaign](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) to subscribe visitors to email and store what they tell you in their profiles, with no separate subscription form to set up. To show a message without input fields, create web content instead. The [Web content template gallery](https://docs.pushwoosh.com/product/content/web-popup-content/web-content-templates/) has ready-made templates for it. ## How a web form works When a visitor submits the form, Pushwoosh does these things in order: 1. Writes each filled-in answer from a **Tag input** field to its tag on the visitor’s browser device right away, even if the visitor isn’t subscribed to web push. An empty answer leaves the tag’s current value as it is. 2. Subscribes the email address to email and creates an email device with the same tag values, if the form has an **Email input**. If [double opt-in](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/#confirm-emails-with-double-opt-in) is on, the visitor first gets a confirmation email. 3. Shows the form’s [Success page](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/#design-the-success-page) once Pushwoosh accepts the submission. If your plan doesn’t include the web platform, the tags aren’t saved, and the submission is still accepted. A form without an **Email input** only saves tags to the browser device, for example to ask visitors about their interests. ## Build a web form To build a form, create it, add the fields, design the Success page, and save it. Split the form into pages if it is long. ### Create a web form 1. Go to **Content → Web content**. 2. Click **Create** and choose the **Web form** type. ![Web content list with the Create menu open and the Web form option highlighted](https://docs.pushwoosh.com/content-web-forms-1.webp) 3. Pick a template: * **Email subscription:** a signup form with a heading, an email field, a consent checkbox, a **Submit** button, and a “Thank you!” Success page. * **Blank form:** an empty form with a title and a **Submit** button. ![Web form template gallery with the Email subscription and Blank form templates](https://docs.pushwoosh.com/content-web-forms-2.webp) 4. Click the name in the top bar and enter a name for the form. Visitors don’t see it. You can’t save the form until it has a name. ### Add form fields A web form has the **Text**, **Button**, and **Icon** blocks and four form blocks: **Email input**, **Tag input**, **Consent**, and **Submit**. 1. Open the **Elements** tab on the right. ![Elements tab of a web form with Text, Button, Icon, Tag input, and Consent tiles, and the Email input and Submit tiles unavailable](https://docs.pushwoosh.com/content-web-forms-3.webp) 2. Drag a block onto the form, or click it to add it to the current page. 3. Select the block on the form. Its options open in the **Settings** tab. * To move the block up or down, use the **POSITION** arrows. * To remove the block, click the delete icon. A form can have only one **Email input** and one **Submit**, so their tiles become unavailable once the form has them. Undo and redo work the same way as in web content. See [Undo and redo](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/#undo-and-redo). Before you save the form * Add at least one **Email input** or **Tag input**. The **Blank form** template has none. * If the form has an **Email input**, enter a **Privacy policy URL** in the [Consent](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#consent) block. The **Email subscription** template has a **Consent** block with an empty URL. Learn more about setting up each block in [Blocks](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/). ### Design the Success page Every web form ends with a **Success** page, which the visitor sees after Pushwoosh accepts the submission. You can’t delete it or make it the start page. 1. Click the **Success** tab, the last tab after **+ Add page**. ![Web form editor with the Success tab selected, a Thank you! message on the canvas, and the Elements tab on the right](https://docs.pushwoosh.com/content-web-forms-6.webp) 2. Edit the page like any other. Use **Text**, **Button**, and **Icon** blocks, for example for a thank-you message with a discount code. A **Submit** button can’t be placed here. ### Split a long form into pages Add pages to ask fewer questions at a time. 1. Click **+ Add page**. The new page is added after the current page, and never after the **Success** page. 2. Add a button with the **Go to page** action to move the visitor forward. A button that goes straight to the **Success** page submits the form. See [Multiple pages](https://docs.pushwoosh.com/product/content/web-popup-content/multi-page/) for how to add, duplicate, and delete pages. A duplicated page is copied without its form blocks. A form has only one **Email input** and one **Submit**, and each tag can be used once. #### How a multi-page form is submitted The form’s one **Submit** button sends all pages together. Before the form is sent: * The visitor can’t go forward while a field on the current page is invalid. Going back is always possible. * On submit, the form first checks every page in the browser. If a field on another page is invalid, the form switches to that page and shows the field’s error text there, and nothing is sent. After the form is sent: * If Pushwoosh rejects the email address, the form switches to the page with the **Email input** and shows its **Invalid email text** there. * If Pushwoosh rejects the submission for another reason, for example because the fields no longer match the saved form, the visitor stays on the current page and sees the **Submit error text**. ### Save the form To see the form at full size before you save, click **Preview**. Preview doesn’t send anything, so test the form on your site. 1. Click **Save** in the top bar. 2. If the **The form can’t be saved yet** banner opens, fix each problem it lists. See [If the form can’t be saved](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/#if-the-form-cant-be-saved). ![The form can't be saved yet banner listing a missing privacy policy URL and a Tag input without a tag](https://docs.pushwoosh.com/content-web-forms-7.webp) 3. Click **Save** again. #### If the form can’t be saved The **The form can’t be saved yet** banner lists every problem that stops the save. Find your message in the table and do what it says. | Message in the banner | What to do | | -------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | | **Add an Email input or a Tag input** | Add at least one field. | | **The form needs exactly one Submit button, not on the success page** | Keep one **Submit** button, on a page other than **Success**. | | **A form with an Email input needs a Consent block with a privacy policy URL** | Add a **Consent** block and fill **Privacy policy URL**. | | **The privacy policy URL must start with https:// or / — otherwise the site shows no link** | Enter the URL as `https://…` or as a path on your site that starts with `/`. | | **Keep only one Email input** | Delete the extra **Email input**. | | **Choose a tag in every Tag input** | Choose a tag in each **Tag input**, or delete the empty field. | | **Tag “…” is chosen in more than one field** | Choose a different tag in one of the fields. | | **“…” is a reserved tag — a form can’t write `PW_FF_*` tags** | Choose another tag. | | **The form needs exactly one success page** | Keep one **Success** page. | When you save, Pushwoosh also checks each chosen tag. If a tag was deleted or hidden, or its name starts with `PW_FF_`, the form isn’t saved, and an error message names the tag, for example `Tag "City" must be an existing, non-hidden tag, not PW_FF_*, used once`. Choose another tag in that field. ## Show the form on your site A form appears on your site only inside an active web campaign. You can use one form in several campaigns. A **Popup** campaign shows it over the page, and an **Inline** campaign shows it as a block inside a page element. Before you start, ask your developer to: * Check that your site runs [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#inline-campaigns-and-web-forms) 3.99.0 or later. An older Web SDK shows the form without its fields. * Add the page element the campaign is placed into, if you plan an **Inline** campaign. See [CSS selector](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#css-selector). Then create the campaign: 1. Go to **Campaigns → Web campaigns**. 2. Click **Create campaign** and choose **Popup** or **Inline**. 3. In **Web content**, choose your form. Forms show **Web form** under their name. 4. Set up the audience and display settings. 5. Launch the campaign. See [Create Web popup campaigns](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) for every step. ## Confirm emails with double opt-in When double opt-in is on for the project, a visitor who submits an email gets a confirmation email and is subscribed only after clicking its link. The confirmation link is valid for 24 hours. Pushwoosh saves the form’s tag values for the email address when the visitor confirms. * If the visitor submits the same address in the same campaign again while unconfirmed, Pushwoosh sends the confirmation email again, at most once every five minutes. Only the link in the newest email works. * An address already confirmed through any web campaign of the project is subscribed right away, without another confirmation email. A confirmation through a subscription form from **Audience → Subscription forms** doesn’t count here. * The confirmation email needs a verified sending domain. While your account has none, Pushwoosh rejects every submission that would send a confirmation email, and the visitor sees the **Submit error text**. * If your plan doesn’t include email, the address isn’t subscribed when the visitor clicks the link, and it stays unconfirmed. This failure doesn’t appear in the campaign’s failed submissions. Double opt-in is one setting for the subscription forms and the web campaign forms of a project. See [Confirm subscribers with double opt-in](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/). ## Track form results Open the campaign’s statistics from **Campaigns → Web campaigns** to see how many visitors submitted the form, why submissions failed, and to export the subscribers. [Web campaign statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/)Visits, impressions, submits, failed submissions, and the subscriber export. --- # Canvas navigation and controls **URL:** https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/ Choose how the mouse, trackpad, and keyboard control the Customer Journey canvas, and find the full list of canvas shortcuts --- The **Canvas settings** panel controls how you pan, zoom, and select elements on the Customer Journey canvas. Open it by clicking the **gear icon** in the bottom-right corner of the canvas. ![Gear icon that opens Canvas settings in the bottom-right corner of the journey canvas](https://docs.pushwoosh.com/customer-journey-canvas-navigation-1.webp) ## What control schemes are available A control scheme is a preset bundle of canvas behaviors: what a left-button drag does, what the scroll wheel does, and whether new steps snap to a grid. Pick one from the **Control scheme** dropdown at the top of the panel. | | Modern (recommended) | Classic | | -------------------------- | ------------------------------------------------------------------------ | ----------------------------- | | Left-button drag on canvas | Selects an area | Moves the canvas | | Scroll wheel & trackpad | Trackpad pans the canvas, mouse wheel zooms it (auto-detects the device) | Scroll wheel zooms the canvas | | Zoom | `Ctrl`/`⌘` + scroll, trackpad pinch, mouse wheel | Scroll wheel | | Snap to grid | On | Off | Existing accounts keep the scheme they were already using; new accounts start on Modern. Changing any individual setting below the dropdown (drag mode, wheel mode, either invert toggle, or snap to grid) switches the dropdown to **Custom setup**. Pick a preset again to reset all of these settings to its defaults. [](https://docs.pushwoosh.com/customer-journey-canvas-navigation-control-scheme.webm "Switching the canvas control scheme between Modern and Classic") ## How to configure panning and zooming Two dropdowns below **Control scheme** let you fine-tune panning and zooming independently of the preset: * **Left-button drag on canvas:** choose whether dragging with the left mouse button draws a selection rectangle (**Selects an area**) or pans the canvas (**Moves the canvas**). * **Scroll wheel & trackpad:** choose **Auto-detect** (trackpad scrolling pans the canvas, a mouse wheel zooms it), **Move canvas (trackpad)** to always pan regardless of device, or **Zoom canvas (mouse wheel)** to always zoom. Two toggles invert the default direction for macOS “natural scrolling” preferences: * **Invert scrolling** flips the direction of trackpad/wheel panning. * **Invert zoom** flips the direction of a plain mouse-wheel zoom only. It does not affect `Ctrl`/`⌘` + scroll or the trackpad pinch gesture, because those are separate bindings and no OS inverts a pinch gesture. ![Invert zoom tooltip: does not affect Ctrl or Cmd plus scroll, or the pinch gesture](https://docs.pushwoosh.com/customer-journey-canvas-navigation-3.webp) Regardless of scheme, you can always pan with `Space` + drag, a right-click drag, or the middle mouse button, and zoom to the cursor with `Ctrl`/`⌘` + scroll or a trackpad pinch. **Snap to grid** applies both while dragging a step and while adding a new one: the step snaps to the nearest free cell so steps never overlap. Turn it off to position steps freely. [](https://docs.pushwoosh.com/customer-journey-canvas-navigation-snap-grid.webm "Snap to grid keeping steps aligned to the nearest free cell while dragging") ## Canvas keyboard shortcuts The **Mouse and keyboard** section of the panel lists every canvas shortcut, grouped into three tabs: **Canvas**, **Selection**, and **Actions**. Shortcuts adjust to your current drag and wheel settings and to your OS (`⌘`/`⇧`/`⌫` on macOS, `Ctrl`/`Shift`/`Del` elsewhere). Common canvas shortcuts: | Action | Shortcut | | --------------------- | ------------------------------- | | Zoom in / out | `Ctrl`/`⌘` `+` / `Ctrl`/`⌘` `−` | | Zoom to 100% | `Ctrl`/`⌘` `0` | | Fit to screen | `Shift` `1` | | Toggle minimap | `Shift` `M` | | Show/Hide UI | `Ctrl`/`⌘` `\` | | Select all / Deselect | `Ctrl`/`⌘` `A` / `Esc` | | Duplicate a step | `Ctrl`/`⌘` `D` | ## How to use the minimap The minimap gives you an overview of the whole journey and a quick way to jump to any part of it. It sits in the bottom-right corner of the canvas, above the zoom controls, and shows every step as a colored dot (colored by its group), with each connection drawn as an arrow along its real route. A rectangle marks the area currently visible on the canvas. It stays visible in [Hide UI mode](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/#what-showhide-ui-does). Toggle the minimap with the button next to the zoom controls or the `Shift` + `M` shortcut; your choice is remembered in this browser. * **Click** anywhere on the minimap to center the canvas on that point. * **Drag** the viewport rectangle to pan the canvas. * **Scroll** over the minimap to zoom the canvas in and out — this respects the [invert zoom](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/#how-to-configure-panning-and-zooming) setting. A red badge on the minimap shows how many steps still need configuration; it’s informational only and isn’t clickable. ![Minimap in the corner of the journey canvas with a badge showing unconfigured steps](https://docs.pushwoosh.com/customer-journey-canvas-navigation-5.webp) On a larger journey, the badge and the ringed dots make it easy to spot which steps need attention without scrolling around the canvas to find them: ![Minimap on a larger, branching journey with two unconfigured steps ringed in red](https://docs.pushwoosh.com/customer-journey-canvas-navigation-6.webp) ## What Show/Hide UI does Press `Ctrl`/`⌘` + `\` to hide the header, sidebar, and all buttons, leaving a clean canvas for presentations or screenshots. Press the same combination again, press `Esc`, or click the button in the corner of the canvas to bring the UI back. Reloading the page also restores it, since the clean-canvas mode is not remembered between page loads. ## How to show metrics on the canvas For a launched or finished campaign, a **Show metrics** toggle sits at the bottom of the Canvas settings panel. Turn it on to keep live stats displayed under every step at once; regardless of the toggle, hovering over a single element also shows its stats. [](https://docs.pushwoosh.com/customer-journey-canvas-navigation-goals.webm "Turning on metrics on the canvas for a launched journey") While metrics are on and the campaign is not a draft or paused, a goal-selector button appears in the top bar. If the campaign has more than one goal, **All goals** is the top item in that menu; if it has only one goal, that goal is selected automatically and **All goals** is not shown. For more on reading these stats, see [Customer Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/). --- # Create a customer journey for your project **URL:** https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project/ Create customer journeys in Pushwoosh. Build from scratch or use templates to launch push, email, in-app, SMS, and WhatsApp campaigns --- Watch this video to explore real-world campaign examples that demonstrate how Pushwoosh helps optimize user engagement and improve results. [Youtube Video: See real-world campaign examples you can create with Pushwoosh to optimize engagement and improve results.](https://youtube.com/watch?v=jtREDe4Z-Ak) Pushwoosh empowers you to create personalized customer journeys that fit your specific scenarios. You have two options to get started: 1. **Build a customer journey from scratch** Take full control of your journey design, crafting multichannel experiences that seamlessly guide users through different stages of the life cycle. With this option, you can: * Design multichannel journeys. * Test and refine your campaigns for optimal results. * Launch campaigns across push notifications, email, in-app messages, and more. Before you begin, [learn how to use Journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements) to ensure a smooth creation process. 2. **Use pre-built customer journey templates** Save time by leveraging pre-made templates for common communication needs like sending a single message via: * Push notifications * Email * In-app messaging * SMS * WhatsApp ![Template gallery for creating customer journeys in Pushwoosh](https://docs.pushwoosh.com/customer-journey-create-a-customer-journey-for-your-project-1.webp) ## Build a customer journey with ready-made journey templates ### Push notification Pushwoosh offers three pre-made journey templates to simplify creating push notification campaigns: * **One-time push campaign** template is ideal for sending a single message at a specific time, often used for broad announcements or special offers. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) * **Recurring campaign** template allows you to set up scheduled messages that will recur over a period. Perfect for ongoing engagement, reminders, or updates. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes) * **Triggered campaign** template allows sending messages in response to specific user behaviors. This flow is designed to increase relevance and engagement by providing personalized communication based on user activity or milestones. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) ![Pre-built push notification templates in Customer Journey](https://docs.pushwoosh.com/customer-journey-create-a-customer-journey-for-your-project-2.webp) Click the template you need to open the Customer Journey canvas. You will see a pre-built journey ready for configuration. Follow the prompts above each element to set up your journey. [Learn more about push notification campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications) ![Customer Journey canvas with pre-built push notification flow](https://docs.pushwoosh.com/customer-journey-create-a-customer-journey-for-your-project-3.webp) ### Email Pushwoosh provides three pre-built email journey templates: * **One-time email campaign** template allows sending a one-off targeted message at a specific time. Ideal for announcements, special offers, or welcome emails. * **Recurring campaign** template allows scheduling regular messages for ongoing engagement. Perfect for newsletters, updates, or loyalty program reminders. * **Triggered campaign** template allows sending emails in response to user behavior. Such campaigns can be used for abandoned cart reminders, post-purchase follow-ups, or birthday greetings. ![Pre-built email journey templates in Customer Journey](https://docs.pushwoosh.com/customer-journey-create-a-customer-journey-for-your-project-4.webp) [Learn more about sending and setting up email campaigns](https://docs.pushwoosh.com/product/messaging-channels/emails) ### In-app message Pushwoosh simplifies in-app communication with a pre-built template for trigger-based in-app campaigns. This allows you to: * Deliver targeted messages at precise moments and reach users within your app based on specific actions or behaviors, maximizing relevance and impact. * Captivate users with timely in-app messages that nudge them towards desired actions. ![Trigger-based in-app campaign template in Customer Journey](https://docs.pushwoosh.com/customer-journey-create-a-customer-journey-for-your-project-5.webp) For a comprehensive guide on creating and managing in-app campaigns within Pushwoosh, [read this guide](https://docs.pushwoosh.com/product/messaging-channels/in-apps/sending-in-apps). ### SMS When creating an SMS campaign, you can choose from three templates based on your messaging goals: * **One-time campaign**\ Send a single SMS message at a specific time. For example, announce a flash sale or notify users about a scheduled app maintenance. * **Recurring campaign**\ Schedule SMS messages to be sent on a regular basis. For example, send monthly billing reminders or provide weekly tips for using a product more effectively. * **Triggered campaign**\ Automatically send SMS messages in response to user actions or events. For example, send an abandoned cart reminder 15 minutes after a user leaves checkout or confirm a booking immediately after it’s made. ### WhatsApp When setting up a WhatsApp campaign, you can choose from three campaign types depending on your communication goals: * **One-time campaign**\ Send a single WhatsApp message at a specific time. For example, announce a product launch, limited-time offer, or important service update. * **Recurring campaign**\ Schedule WhatsApp messages to be sent on a regular basis. For example, send weekly check-ins, monthly subscription reminders, or recurring appointment notifications. * **Triggered campaign**\ Automatically send WhatsApp messages based on user actions or specific events. For example, confirm a booking after it’s made or send an order update. --- # Prepare your project **URL:** https://docs.pushwoosh.com/product/customer-journey/getting-started/ Get your project ready for Pushwoosh Customer Journey Builder with this step-by-step guide for successful campaign setup --- Tip Marketing and developer teams should work closely together while preparing a project. Before creating your first campaign with Pushwoosh Customer Journey Builder: 1. Create a project and configure platforms in your Pushwoosh account. 2. Integrate the Pushwoosh SDK into your app or website. 3. Make sure users can subscribe to your notifications. 4. Prepare segmentation, content, and anything else you may need to launch journeys. Below you will find detailed instructions for each of these steps. ### Create a project and configure platforms (if necessary) Note Skip this step if you have already configured the project where you want to create a journey. #### Creating a project Create a project in your Pushwoosh account using [this instruction](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project). You can configure several platforms within one project. For example, if you select *Mobile push* for start configuration, you can also set up Web and Email platforms at any time. ![Project creation screen in Pushwoosh account](https://docs.pushwoosh.com/customer-journey-getting-started-1.webp) #### Configuring platforms Developer assistance needed You’ll need your developer team’s help to configure your Pushwoosh project. Share the configuration guide links with them to get started. Configure the platforms to which you want to send notifications: * To configure mobile platforms, follow the [iOS configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration) and [Android configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/) guides. * To configure web push notifications, refer to the [Web push configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/) guide. * If you want to send emails, verify your email address and domain by following the [Email configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification) guide. * To send WhatsApp messages, ensure the [WhatsApp platform](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) is properly configured. * To send SMS, connect your SMS provider by following the [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration) guide. [More about configuring platforms](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms) ### Integrate the Pushwoosh SDK Developer assistance needed You’ll need your developer team’s help to integrate the Pushwoosh SDK. Share the SDK integration guide links with them to get started. Make sure the appropriate SDK is integrated into your app or website: * [iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk) * [Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk) * [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/) [Learn more about Pushwoosh SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview) ### Subscribe users Users should be able to subscribe to your app’s or site’s notifications — otherwise, you won’t be able to send them messages. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) Developer assistance needed You might need your developer team’s help to register user devices using Pushwoosh SDK and API. Share [this link](https://docs.pushwoosh.com/developer/first-steps/subscribe-users) with them to get started. ### Prepare segmentation and content Depending on the elements you plan to use, prepare **Events**, **Tags**, **Segments**, **Push Presets**, **Email Presets**, and **In-Apps** in your project. #### Events **Events** are actions users perform in the app or on the website. They are recorded by the Pushwoosh SDK and can trigger various behavior-based communications. Each configured Event can be used in the following elements of a journey: * [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) * [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) * [Conversion Goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals) [**More about Events**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events) ![Events list](https://docs.pushwoosh.com/customer-journey-getting-started-2.webp) #### Tags and Segments **Tags** contain data that you associate with a particular user or device: username, ID, city, preferences, etc. Using Tags, you can create Segments and [update user profiles](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) in a journey. [**More about Tags**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) **Segments** (previously called Filters) are groups of subscribers complying with the conditions you specify. You can build Segments based on Tag values, triggered Events, or both. You can also use automatic [RFM Segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation). Segments will help you target specific users with specific messages increasing communication relevance and thus boosting open rates. Segments can be used in the following elements of a journey: * [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) * [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) [**More about Segments**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) ![Segments list in Pushwoosh showing filters and criteria](https://docs.pushwoosh.com/customer-journey-getting-started-3.webp) #### Push & Email content Pushwoosh empowers you to create reusable **push content**. You can craft the text and configure settings using our push content editor and then either send the push notification immediately or save it for future campaigns. [Learn more about creating push content](https://docs.pushwoosh.com/product/content/push-presets) You can craft your **email content** using the editor that best fits your needs: * The Drag & drop editor, perfect for marketers with no coding skills. [Learn how to create content with the Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/). * The HTML code editor, offering the ability to generate emails using code and granting greater control over email customization. [Learn how to create content with the HTML code editor.](https://docs.pushwoosh.com/product/content/email-content/html-code-editor) #### In-Apps **In-Apps** are interactive content pages you can fully customize to suit your business needs and app experience. Pushwoosh provides default in-app templates you can personalize or use as a starting point to create your own. In-apps are required to [display content pages within your app](https://docs.pushwoosh.com/product/messaging-channels/in-apps) to your users. [**More about in-app messages**](https://docs.pushwoosh.com/product/content/in-apps) ![Rich Media templates gallery in Pushwoosh](https://docs.pushwoosh.com/customer-journey-getting-started-4.webp) ### Check if everything is working Once everything’s set up, check whether it works as it is supposed to. There are several options to test sending messages to your app or site subscribers. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration) Note You might need your developer team’s help to test push notifications. Share [this link](https://docs.pushwoosh.com/developer/first-steps/test-your-integration) with them to get started. ### Start creating your first journey Now you are all set to create your first customer journey! Go to the **Customer Journey Builder** section and click **Create Campaign**: ![Customer Journey Builder with Create Campaign button](https://docs.pushwoosh.com/customer-journey-getting-started-5.webp) Check out this [step-by-step guide](https://docs.pushwoosh.com/product/customer-journey/how-to-try-it-out) on creating a test journey using our demo app. --- # Create a test journey **URL:** https://docs.pushwoosh.com/product/customer-journey/how-to-try-it-out/ Build a test customer journey in Pushwoosh with the Push On demo app and learn key Builder features step by step to practice safely --- You can play around with Pushwoosh Customer Journey Builder and learn its core functionality before creating journeys for real users. To do this, use the **Push On** mobile app we developed specifically for testing Pushwoosh features. Follow the steps below to learn how to create your first test journey for mobile users. Note You can also create a test journey for web users using our demo website: ### 1. Install the Push On demo app 1. Download the app by scanning QR code or clicking the link below: ### iOS ![iOS App](https://docs.pushwoosh.com/shared-11.webp)[Download for iOS](https://apps.apple.com/ru/app/push-on/id1122054952?l=en) 2. In your Pushwoosh account, open the default **Pushwoosh Demo App** and find your **Application code** at the top left corner of the Control Panel. ![Pushwoosh Control Panel showing the Application code in the Demo App](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-1.webp) 3. Open the Push On app. If you see a pop-up about sending pushes, confirm your permission. After that, enter your **Application code** and click **Submit**: ![Push On app screen to enter the Application code and submit](https://docs.pushwoosh.com/shared-26.webp) 4. Make sure the Push On app is allowed to send push notifications. ### 2. Create a journey Let’s say you have a shopping app, and you are going to convert your app users into buyers. A user comes to your app and adds some products to their shopping cart but does not complete the purchase for some reason. It’s time to send a push notification reminding them that the shopping cart is waiting for their return! Your marketing goal in this case is a purchase successfully completed by the user. The steps below describe creating a customer journey for our example case. 1. In your Pushwoosh account, go to the **Pushwoosh Demo App**. Open the **Customer Journey Builder** section and click **Create Campaign**: ![Customer Journey Builder with Create Campaign option in Pushwoosh Demo App](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-2.webp) 2. We want the journey to start once a user adds an item to the cart. For this purpose, we will use a **Trigger-based Entry**, which is added automatically when creating a new campaign. Double-click on the Entry element and select the **ProductAdd** Event: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-3.webm "Selecting the ProductAdd event for the Trigger-based Entry") Note The **ProductAdd** Event is triggered when a user adds some item to their cart in the Push On app. This Event is available by default for the Pushwoosh Demo App. In a real project, you can [configure your own Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) and use them in journeys. 3. Now let’s divide users into those who have completed the checkout process and those who have abandoned their cart. To do this, add the **Wait for Trigger** element, choose the **CheckoutSuccess** Event, and set the element to wait for 1 minute: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-4.webm "Configuring Wait for Trigger with CheckoutSuccess event and 1-minute wait") Note The **CheckoutSuccess** Event is available by default for the Pushwoosh Demo App. In a real project, you can [configure your own Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) and use them in journeys. If a user completes the checkout within 1 minute after adding something to a cart, they will go to the **Triggered** branch. Otherwise, they will go to the **Not triggered** branch. 4. Let’s send a push notification to users from the **Not triggered** branch to encourage them to return to shopping: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-5.webm "Adding a push notification using the Cart Recovery - 1 min preset") Note In our example, we used the **Cart Recovery - 1 min** [Push Preset](https://docs.pushwoosh.com/product/content/push-presets) already preconfigured in the Pushwoosh Demo App. You can create your own Presets and use them in journeys. 5. Add one more **Wait for Trigger** element to track the efficiency of your communication – check out whether users complete their purchases after your push notification. This time let’s wait for 5 minutes after sending the first push: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-6.webm "Adding a second Wait for Trigger for 5 minutes after the first push") 6. If a user does not complete the purchase within 5 minutes, send another push to recover the abandoned cart: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-7.webm "Configuring the second cart recovery push notification") Note For this push notification, we used the **Cart Recovery - 5 min** [Push Preset](https://docs.pushwoosh.com/product/content/push-presets) already preconfigured in the Pushwoosh Demo App. You can create your own Presets and use them in journeys. 7. End the journey here by adding the **Exit** element, or simply leave the branch without an outgoing connection — both mark the end of the journey: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-8.webm "Adding the Exit element to end the journey") 8. Track your results with **Conversion Goals** – set up the **CheckoutSuccess** Event as an indicator of your journey’s success and check out how many users reach this goal within the journey: [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-9.webm "Setting a Conversion Goal with the CheckoutSuccess event") 9. Click **Launch campaign** to run the [Pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check). 10. If the journey is ready, a confirmation dialog opens. Click **Launch**. [](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-10.webm "Using Pre-launch check to validate and launch the campaign") ### 3. Test your journey You can now test the journey using the Push On app. 1. If you haven’t seen the pop-up about sending pushes yet, go to the **Settings** section and click the **Communication manager** button: ![Push On app Settings screen with Communication manager](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-11.webp) Check the box to enable push notifications and click **Confirm**. 2. Go to the **Explore** section and add any item to the cart: ![Push On app Explore section adding an item to the cart](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-12.webp) 3. Open the **Cart**, click **Checkout**, and select **Continue as guest**. This will trigger the **ProductAdd** Event. * If you do nothing else, after a minute you will receive the first push notification. If you wait another 5 minutes, you will receive the second push notification. * If you click **Proceed**, you will trigger the **CheckoutSuccess** Event and may not receive push notifications depending on the time passed after triggering the **ProductAdd** Event. 4. If you want to go through the journey steps again as a different user, go to the **Settings** section and click **Remove my personal data**. After that, you can start the journey as a new user (don’t forget to enable push notifications again, as described in step 1). ![Push On app Settings screen with Remove my personal data option](https://docs.pushwoosh.com/customer-journey-how-to-try-it-out-13.webp) Caution If you do not see an expected message, open the Push On app. Click **Settings** → **Change Account**, and re-enter your **Application code**. Then try sending the push again. ### Next steps Now you can start creating a journey for real users. The following articles will help you: * [Journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) * [Journey settings](https://docs.pushwoosh.com/product/customer-journey/journey-settings/) You can also check out examples of journeys for each stage of the user lifecycle: * [Onboarding](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) * [Activation](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/) * [Engagement](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh/) * [Retention](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/) * [Re-engagement](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh/) --- # Journey editing **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-editing/ Learn how to safely edit Pushwoosh journeys. Pause a campaign, update elements, manage audiences, and resume with confidence. --- Both draft and active journeys can be edited. You may need to update journey elements in the following cases: * Test the effectiveness of your communications * Adjust a journey to some special events and changing conditions * Edit occasional errors ## Editing a draft journey To edit an element of a journey that has not yet been launched, simply double-click on it. You can learn more about setting up each element in the [journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) article. If you need to delete an element of a draft journey, select this element and click on the trash icon: ![Delete journey element using trash icon in draft journey](https://docs.pushwoosh.com/customer-journey-journey-editing-1.webp) ## Editing an active journey To edit an active journey, you first need to pause it. Click on the campaign’s dropdown menu and select **Pause to edit.** ![Campaign dropdown menu with Pause to edit option](https://docs.pushwoosh.com/shared-58.webp) While the journey is paused, no new users enter it. Users who already entered stay in the journey and continue from where they left off once you resume it. See [What happens to users already in the journey when you pause it](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#what-happens-to-users-already-in-the-journey-when-you-pause-it) for details on specific elements. ### Edit elements on the canvas Modify and delete elements the same way as in a [draft journey](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#editing-a-draft-journey). Double-click an element to open its settings. Note If you change the content of a push, in-app, email, or SMS and those changes apply to the current version, users still in the journey receive the updated content after you resume. If resuming starts a new version instead, users already in an older version keep the content from the version they entered. See [Journey history and versions](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/). ### Delete or add elements If you delete an element, connect the remaining elements to each other or to a new element if you add one: [](https://docs.pushwoosh.com/customer-journey-journey-editing-2.webm "Connect remaining elements after deleting a node on journey canvas") Leaving a branch without an outgoing connection is also a valid way to end it. On the canvas, an open branch end shows a small hint icon with a counter of how many users left there. In statistics, these departures count under [**Exits**](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#audience), not as an error. Caution After deleting an element, all statistics on it will be lost. If you need these statistics, we recommend saving them somewhere before editing a journey. ### Save changes Pushwoosh saves your changes automatically while you edit a draft or paused journey, so saving by hand isn’t required. The status below the campaign name tells you what to do: * **Not saved:** autosave hasn’t saved your edits yet. No action needed. It saves automatically about 10 seconds after you start editing. Closing the tab or navigating away before then doesn’t lose the edit. It stays cached in this browser and picks up again when you reopen the journey here. * **Saved:** your edits are saved. This appears briefly after each successful save, then the status goes back to showing the campaign name. * **Saved only in this browser:** autosave couldn’t reach the server. The status turns orange in the header, and a banner appears below the canvas with a **Save again** button. ![Saved only in this browser status shown in orange below the campaign name in the header](https://docs.pushwoosh.com/customer-journey-journey-editing-12.webp) The banner below the canvas repeats this information with a way to retry: ![Save status banner showing a failed save with a Save again button](https://docs.pushwoosh.com/customer-journey-journey-editing-10.webp) A change marked **Saved only in this browser** stays in this browser’s local storage: it survives a reload here, but won’t reach other browsers or devices until a save succeeds. Click **Save again** to retry immediately. Otherwise, Pushwoosh keeps retrying in the background and clears the banner once a save goes through. If you try to leave the page while the latest change hasn’t reached the server, Pushwoosh asks whether to **Leave anyway** or **Stay**: ![Leave-page confirmation shown when the latest change couldn't be saved](https://docs.pushwoosh.com/customer-journey-journey-editing-11.webp) Leaving without saving doesn’t discard the edit. It stays cached in this browser and picks up again when you reopen the journey here. ### Resume the campaign When you finish editing, click **Resume campaign** at the top right of the canvas. ![Resume campaign button on journey canvas](https://docs.pushwoosh.com/customer-journey-journey-editing-4.webp) For journeys with **Audience-based entry**, the resume step shows additional audience options. See [Resume options for Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#resume-options-for-audience-based-entry). Depending on what you changed, resuming either keeps the campaign on its current version or starts a new one. See [Journey history and versions](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/) for what each option means and how to manage versions afterward. ## What happens to users already in the journey when you pause it Pausing a journey does not stop users who already entered it: they continue from where they left off once you resume it. What happens to a user in the meantime depends on which element they were waiting at when you paused. ### A user is waiting at a Time Delay Pausing the journey does not pause the [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) timer: the send time is fixed the moment the user enters the delay point, not when the journey resumes. * If that time falls inside the pause window, the user waits at this step and the message goes out immediately once you resume the journey. * If it falls after you resume, the message goes out at the originally scheduled time, as if the pause never happened. For example, a user enters a 24-hour Time Delay on Monday at 10:00, so the message is scheduled for Tuesday at 10:00. Pause the journey Monday at 15:00 and resume it Wednesday: the message sends immediately on resume. Pause it Monday at 15:00 and resume Tuesday at 08:00 instead: the message still sends at the originally planned Tuesday 10:00. Caution A user waiting at a Time Delay is lost if you pause the journey for longer than 7 days (this happens silently, with no log entry or metric), if you select **Stop campaign** while paused, or if you delete the Time Delay element during editing. See [Pausing a journey while a user is waiting](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#pausing-a-journey-while-a-user-is-waiting) for details. ### A user is waiting at Wait for Trigger If the event waiting period of the [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) element ends while the journey is paused, the user waits at this step. Once you resume the journey, the user continues along the **Not triggered** branch. Caution As with a Time Delay, the user is lost if the pause lasts longer than 7 days, if you select **Stop campaign** while paused, or if you delete the Wait for Trigger element during editing. ### A user is waiting on the Opened/Not opened split A user who received a message just before you paused the journey, in a flow with [**Split flow if message is opened**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#split-flow-by-opens-or-ignores) turned on, waits at this step if the wait time for that split ends while the journey is paused. Once you resume the journey, the user continues along the **Not opened** branch. Caution As with a Time Delay, the user is lost if the pause lasts longer than 7 days, if you select **Stop campaign** while paused, or if you delete the Push element during editing. ## Resume options for Audience-based entry After you pause the journey, edit it (e.g. change message content or add steps), and click **Resume campaign**, the [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) element shows options that depend on the current **Entry schedule** setting. These options control how users are treated when the journey resumes. ### No scheduled entry date If **Entry schedule** is **One-time entry** and **Immediately** (users entered at launch, no date was set), choose how the audience is handled when the journey resumes: * **Do not re-enter audience into campaign upon resuming (default)**\ Users who previously entered will not re-enter the journey. They will continue from where they left off. * **Re-enter audience into campaign upon resuming**\ Users who still match the segment criteria will be allowed to re-enter the journey upon resuming if the [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) settings allow re-entry, and the user is not currently in the journey. This means users who previously entered and are no longer active in the journey can be re-added if both conditions are met. ![Choose how the audience is handled when the journey resumes](https://docs.pushwoosh.com/customer-journey-journey-editing-8.webp) ### Scheduled entry date If **Entry schedule** is **One-time entry** and **Schedule for a specific day**, select what happens for users whose scheduled entry time (in their local timezone) has already passed: * **Exclude users whose local time has passed**\ Users whose local scheduled time (e.g. 10:00) has already passed will not enter the campaign. * **Let users enter immediately**\ Users whose local time has already passed will enter the campaign immediately when it resumes. Important If you previously set **Schedule for a specific day** to a date (e.g. August 5) and that date has already passed, selecting a new future date (e.g. August 8) triggers a different system behavior: the campaign will reprocess the audience. As a result, all users who match the audience segment at the time of the new launch will be added to the campaign, provided that the [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) settings allow re-entry, and the user is not currently in the journey. This applies even if they had already entered on the original date. ![Editing when One-time entry and Schedule for a specific day is set](https://docs.pushwoosh.com/customer-journey-journey-editing-9.webp) In the **After resuming the campaign** section, choose how the audience is handled when you click **Resume campaign**: * **Do not re-enter audience into campaign upon resuming (default)**\ Users who previously entered will not re-enter the journey. They will continue from where they left off. * **Re-enter audience into campaign upon resuming**\ Users who still match the segment criteria will be allowed to re-enter the journey upon resuming if the [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) settings allow re-entry, and the user is not currently in the journey. ### Entry close date If the point has [When entry closes](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#when-entry-closes) set to **On a specific day**, clicking **Resume campaign** validates the close date the same way a first launch does. If the close date isn’t later than the entry start date in every timezone it applies to, the resume is rejected with the same error as at launch: **End entry must be later than start entry**, or **End entry is not later than start entry in timezones: …** naming the affected ones. Changing only the close date doesn’t reprocess the audience the way a new **Schedule for a specific day** start date does. Whether users who already passed through re-enter still depends only on the **Do not re-enter** / **Re-enter** choice above, not on the close date. ## View journey change history and versions Click the **clock icon** at the bottom-right corner of the canvas to open the history panel, on the right side of the journey builder. See [Journey history and versions](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/) for what the panel’s tabs show, whether you get a **Versions** tab at all, how to read the [change log](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#change-log), and how to switch between [versions](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#view-versions). --- # Getting started with Journey elements **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/ This article describes the elements you can use to create customer journeys --- Customer journeys in Pushwoosh are built using modular journey elements. These represent entry points, communication channels, and flow controls that define how users move through your journey. ## Working with Journey elements ###### Adding elements to a journey To add an element to your journey, either drag it from the left panel to the canvas, or click **Connect this** on an existing element and choose the next step from the list. The left panel groups elements into sections: **Entry**, **Channels**, **Flow**, and **Integrations**. Click a section’s header to collapse or expand it. This browser remembers which sections you left collapsed the next time you open a journey. ###### Deleting an element To **delete** an element, click it once and press the **Bin icon** on the top. ![Interface showing the Bin icon for deleting journey elements from the canvas.](https://docs.pushwoosh.com/journey-elements-README-2.webp) ###### Editing an element To **edit** an element, click it twice and fill in the fields of the pop-up window that appeared. [](https://docs.pushwoosh.com/journey-elements-README-3.webm "Demonstration of editing journey elements by double-clicking and filling in the configuration popup.") ## Journey elements Journey elements are grouped into four categories, based on the role they play in a journey. ### Entry Entry elements control when and how users enter a journey: on a trigger event, for a predefined audience, or through an API call from an external system. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) ### Channels Channel elements send the actual message to a user, such as push, email, SMS, WhatsApp, and other messaging channels, plus related options like issuing a wallet pass or running a multi-armed bandit test. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/) ### Flow Flow elements decide what happens next in a journey: they branch users by segment or tag, test message variants, pause the journey for a set delay, or update a user’s profile. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/) ### Integrations Integration elements sync journey data with third-party CRM, analytics, and data warehouse tools like HubSpot, Salesforce, and BigQuery. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/) ## Canvas hotkeys To ease your journey configuration, use the hotkeys allowing instant managing journey’s parts with no clicks: * Select a journey element and copy-paste it with **Ctrl+C/Ctrl+V** (Cmd+C/Cmd+V). * Select several journey elements by holding Shift/Cmd and copy-paste the whole selection with **Ctrl+C/Ctrl+V**. * Copy and paste elements between journeys (do not switch tabs while copying). * Delete journey elements from the canvas by pressing your system **Delete** hotkey. * Select a single journey element and copy its `/` pair to the clipboard with **Ctrl+I** (Cmd+I). [](https://docs.pushwoosh.com/journey-elements-README-4.webm "Demonstration of keyboard shortcuts for copying, pasting, and deleting journey elements on the canvas.") --- # Channel elements overview **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/ Use channel elements to send push, email, SMS, in-app, App Inbox, WhatsApp, Telegram, LINE, Kakao, Viber, and Facebook Messenger. --- Channel elements allow you to integrate different communication methods directly into customer journeys. ### Push notifications Send personalized push messages to engage users in real time. Configure delivery timing, apply vouchers, and use frequency controls to optimize message performance. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) ### Multi-Armed Bandit Automatically send the best-performing push variant in journeys using real-time open or conversion rates, without manual A/B testing. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/multi-armed-bandit/) ### Email Send emails with tailored content, run A/B subject line tests, manage sender details, enable BCC, apply frequency limits, and integrate voucher codes. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) ### In-app messages Show rich-media messages directly inside the app. Ideal for onboarding, promotions, announcements, or contextual guidance when users are active in the app. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) ### App Inbox Place a message directly in a user’s in-app inbox, with its own expiration and frequency capping. Users without an inbox device skip the point instead of dropping off. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/) ### SMS Send short, timely messages via SMS for critical updates, confirmations, reminders, or to reach users not active in your app. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/) ### WhatsApp Use pre-approved templates or custom replies to engage users with WhatsApp messaging. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/) ### Viber Send Viber messages using a Rakuten-approved template or freeform text, with independent pricing and message type controls. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/) ### Telegram Send plain text messages to users who started your bot, with optional vouchers and journey statistics. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/telegram/) ### LINE Engage users with LINE messages using presets or free-form text. Include vouchers and configure delivery-based flow branching. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/line/) ### Kakao Send approved Kakao messages via Customer Journey using Kakao templates with dynamic variables and optional vouchers. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/kakao/) ### Facebook Messenger Send Facebook Messenger messages to users who messaged your connected Facebook Page, with presets for buttons, media, and cards and message tags for sends outside the 24-hour window. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/) ### Data to app > Developer support required to implement this feature. Send silent pushes or JSON data that triggers in-app actions such as syncing, unlocking features, or updating content. Ideal for background updates, beta testing, and feature rollouts. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) ### Webhook The **Webhook** element sends journey data to external systems like analytics platforms, CRM tools, or messaging services. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) ### Issue Wallet Pass Issue an Apple or Google Wallet pass for a traveler, then deliver the install or save link with a follow-up Send Push or Send Email element. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) ### Live Activity Start, update, or end an iOS Live Activity, the lock-screen and Dynamic Island card for a flight, delivery, or ride, built from a published widget schema. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) ## Omnichannel campaigns Combine multiple channels in one journey to reach users through their preferred touchpoints and engage those unavailable on one channel via another. This ensures consistent communication, improves message visibility, and increases the effectiveness of onboarding, re-engagement, or promotional campaigns. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) --- # App Inbox **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/ Place App Inbox messages in a customer journey with expiration, frequency capping, and skip handling for users without inbox. --- Use the **App Inbox** channel element to place a message directly in a user’s in-app inbox from a journey, without going through push. [Learn how App Inbox works](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/). Note **Requirements** App Inbox needs the Message Inbox SDK module integrated in the app. Users with no device on an inbox platform (iOS, Android, Huawei, or web) skip this point. The card layouts need Web SDK 3.75.0, Android SDK 6.12.0 with the `pushwoosh-inbox-ui` module, or iOS SDK 7.0.40 or later with the PushwooshInboxKit module, and the **Carousel** layout needs iOS SDK 7.1.2 or later. An older SDK still receives the message but shows it without the layout. See [What happens to users at this point](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/#what-happens-to-users-at-this-point). Drag the **App Inbox** element onto the canvas from **Channels**, then click it to open its settings. ## Select or create App Inbox content In **Preset**, select an existing [App Inbox preset](https://docs.pushwoosh.com/product/content/app-inbox-presets/), or click **Create content** to open the editor. **Edit content** opens the same editor for the selected preset. The fields match [Inbox Presets](https://docs.pushwoosh.com/product/content/app-inbox-presets/#choose-a-layout-type): layout type, title, message, media, and the rest. To change the order of presets in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every App Inbox preset dropdown, in one-time messages and journeys alike. ![App Inbox element Preset field with Welcome to Pushwoosh selected and an arrow on the Name A→Z sort button](https://docs.pushwoosh.com/journey-elements-app-inbox-1.webp) ## Choose a tab If the app’s inbox is split into [tabs](https://docs.pushwoosh.com/product/content/app-inbox-presets/#set-up-inbox-tabs), choose one in **Tab**. The message shows in that tab and in **All**. Leave **No tab** to show it only in **All**. Tabs show in the inbox of iOS SDK 7.3.0 and Android SDK 6.17.0 or later. The Web SDK inbox has no tabs. If the app has no tabs yet, the field shows **Set up tabs** instead, which opens the tab settings. If the tab is deleted later, this element can’t send its message until you choose another tab. ![Tab field in the App Inbox element with Offers selected](https://docs.pushwoosh.com/journey-elements-app-inbox-5.webp) ## Set message type Choose **Marketing message** or **Transactional message** for this element. * **Marketing message:** respects frequency capping. Excludes the [global control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message:** bypasses frequency capping. Sent to the global control group too. No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![Marketing message and Transactional message options in the App Inbox journey element](https://docs.pushwoosh.com/journey-elements-app-inbox-2.webp) ## Set on click actions In **On click actions**, choose what happens when the user taps the message. **Mobile:** * **No action on click:** nothing opens when the user taps. * **Open Deep link:** open a deep link in the app. * **Open URL in browser:** open a URL in the device browser. * **Show in-app content:** show an [in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) message you select. The in-app list is sorted by **Newest first** until you pick another order with its sort button. **Web:** * **No action on click:** nothing opens when the user taps. * **Open URL in browser:** open a URL in the browser. * **Show web pop-up:** show a [web popup](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) you select. The list starts with **Recently updated** until you pick another order with its sort button. Pick web content here, not a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). The Web SDK shows a web form only inside a web campaign, so a web form picked here doesn’t open. ## Set expiration In **Expires**, choose how long the message stays in the inbox: * **After 7 / 14 / 30 / 90 days**, or a specific date. * Leave **After 30 days** unless you need a shorter or longer window. After that time, Pushwoosh removes the message from the inbox. ![Expires field in the App Inbox journey element](https://docs.pushwoosh.com/journey-elements-app-inbox-3.webp) ## Set frequency capping In **Frequency capping**, choose how often this user can get App Inbox messages. Limits apply to the [App Inbox frequency capping channel](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#app-inbox), separate from push, email, and other channels. * **Use global frequency capping settings:** apply the project-wide App Inbox limit. * **Send messages without global frequency capping:** send even if the user already hit that limit. Use with caution. * **Use custom frequency capping:** set a limit only for this element. ![Frequency capping field in the App Inbox journey element](https://docs.pushwoosh.com/journey-elements-app-inbox-4.webp) Once you configure the element, click **Apply**. ## What happens to users at this point The table covers edge cases: no device to place the message on, more than one device, and what happens if you pause or stop the journey. | When | What happens | | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Message is placed in the inbox | The user moves to the next step right away (`Sent`). They do not need to open the app first. | | No inbox on any device | Email-only or messenger-only users, and other users with no device on an inbox platform, skip this step and continue. They show up as **skipped** in the stats. Web counts when the site has the SDK. | | More than one device | The message appears in the inbox on every device that supports it. **Sent** counts the person once, not each device. | | You pause or finish the journey | Messages already in the inbox stay until they expire. Stopping the journey does not remove them. | ## Metrics on the canvas [App Inbox statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/) Note To react to inbox engagement in the journey, split on an event, a tag, or another channel’s engagement. App Inbox does not branch on read or open yet. --- # Data to app **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/ Send custom data and silent push notifications with the Data to app element to trigger in-app actions, update content, and improve engagement --- Developer assistance needed You’ll need assistance from your development team to use the Data to app element. Please share this guide with them. The **Data to app** Journey element allows you to send custom data or [silent push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications) and deliver data that your app can interpret and act upon, enhancing user engagement and app functionality. ## What is Custom Data? Custom data is additional information sent along with a push notification. This data is typically in JSON format and can include various parameters that instruct the app to perform specific actions upon receiving the notification. When a push notification with custom data is received, the app processes the JSON payload to perform predefined actions. This allows for dynamic content updates, navigation, and other interactive features without requiring the user to manually open the app. ## How to send custom data with the Data to app element 1. Add the Data to app element to the Journey canvas. ![Add Data to app element on the journey canvas](https://docs.pushwoosh.com/journey-elements-README-20.webp) 2. Paste your JSON code into the provided field. ![Enter JSON payload in the Data to app element](https://docs.pushwoosh.com/journey-elements-README-21.webp) 3. After entering your JSON data, click **Save** to apply the configuration. ### Use vouchers Add voucher codes from a predefined pool to your custom data payload to support in-app promotions, discounts, or loyalty rewards—delivered silently or triggered by specific app logic. To set up vouchers: 1. Include the `{{voucher}}` placeholder in your JSON payload. **Example:** ```json { "message": "Congratulations! You've received a special discount: {{voucher}}" } ``` 2. In the Data to app element settings, toggle **Vouchers** to ON. 3. Choose the relevant **Voucher Pool** that contains the available codes (make sure it’s created in advance). 4. (Optional) Use **Assign Tag** to label users who receive vouchers for tracking or segmentation. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) ## Data to app usage examples #### Send silent push notifications Silent push notifications are a type of push notification that does not alert the user with sounds, vibrations, or visual notifications. Instead, they are delivered quietly in the background. Silent push notifications paired with custom data are used to update the app content, sync data, or trigger specific app behavior without interrupting the user. #### Track uninstalls Send a silent push daily to your user base to verify if the app is installed on the user’s device. #### Update your application proactively In certain situations, you may need to update your application on users’ devices without requiring any action from them. A silent push with Custom Data is ideal for this purpose. You can send it to all users or a specific user segment. The push notification will open automatically, launching your application in the background and updating its content within a minute, all without any user intervention. ##### JSON example ```plaintext { "Action": "UpdateApp" } ``` #### Test a new section or module in your application With Custom Data, you can open a new feature for beta testing. Select a target segment and send them a push notification announcing the added functionality. If a user taps the message, they will gain access to the section you opened exclusively for them. ##### JSON example ```plaintext { "EnableNewFeatureSection" : "Yes" } ``` #### Update app icon Change the app icon dynamically. ##### JSON example ```plaintext { "UpdateAppIcon": "https://example.com/new-icon.png" } ``` #### Deliver promo codes and discounts You can send push notifications with promo codes or discounts that are automatically applied when the user taps on the notification. This approach encourages user engagement and makes it easier for users to take advantage of special offers, enhancing their overall experience. ##### JSON example ```plaintext { "ApplyPromoCode": "DISCOUNT2024" } ``` #### Grant bonus points to users Send push notifications with custom data to grant bonus points. When the user taps the notification, the bonus points are credited to their account automatically. ##### JSON example ```plaintext { "AddPromo" : "+1000" } ``` Learn more about how to [customize your messages with custom data](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data) --- # Email **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/ Send personalized emails in journeys with A/B testing, vouchers, delivery rules, and frequency caps to boost engagement and conversion rates --- **Email** element indicates a point to communicate to a customer with an email message. To send an email at some moment of the customer journey, add the **Email** element to the canvas following the element you consider to be the appropriate basis for communication. ## Select email content Choose the [**email content**](https://docs.pushwoosh.com/product/content/email-content) you’d like to use. To change the order of email content in the dropdown, click the sort button above it. The dropdown is sorted by **Newest first** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every email content dropdown, in one-time messages and journeys alike. If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, only **Approved** versions can be selected here. Other versions show their status and stay disabled. ![Email element with the Email preset field and its Newest first sort button outlined](https://docs.pushwoosh.com/journey-elements-README-14.webp) Caution Please be aware that if any segmentation rules are saved in the email content you choose for a journey, they **won’t be applied** to the Journey. ## Set message type Choose **Marketing message** or **Transactional message**: * **Marketing message**: Respects subscription preferences, opt-outs, frequency capping, and the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts). Not sent to the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). If you choose this, select a subscription category (e.g. Product Updates, Newsletter) from the dropdown so the message aligns with [subscriber preferences](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). * **Transactional message**: Sent to all users regardless of subscription status. Delivered to the control group. [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) Reaching unsubscribed users To reach users who unsubscribed from marketing emails, choose **Transactional message**. ![Email element configuration with Marketing message selected and Subscription category dropdown](https://docs.pushwoosh.com/emails-send-email-28.webp) ### Select subscription category If you chose **Marketing message**, pick a category from the dropdown (e.g. Product Updates, Newsletter, Promotional) so the message aligns with your subscribers’ preferences. Categories are defined in the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). ## Set the subject line In **Subject line**, choose how the subject is set for this step: * **Custom**: Type a subject in the **Subject** field. The field is pre-filled with the subject from the preset, but you can edit it. A custom subject overrides the preset and applies to all emails from this step. ![Subject line settings in the Email element with Custom and From preset options and Subject field](https://docs.pushwoosh.com/emails-send-email-32.webp) * **From preset**: Use the subject saved in the selected email preset. If you update the subject in the preset later, the change applies automatically. If the preset has no subject, add one to the preset first. ![Subject line settings in the Email element with Custom and From preset options and Subject field](https://docs.pushwoosh.com/emails-send-email-33.webp) Keep your subject line short, clear, and engaging. It’s the first thing recipients see in their inbox. How to set up an A/B/n test for subject lines You can [run A/B/n tests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) to compare two different subject lines and find out which one performs better. To set it up: 1. In the Journey builder, select the [A/B/n split element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/). 2. Add two Email elements after it, one for each version of your email. 3. In each Email element, set a different subject line. **Example** * **Email 1 subject line:** *“New Year, New Deals – Save Big Today!”* * **Email 2 subject line:** *“Don’t Miss Our Biggest New Year Sale!”* Once your test is live, you can track which subject line gets a better open rate and optimize future campaigns based on the results. ## Define sender details and reply address In **Sender info**, choose how sender details are set: * **Specify manually**: Type the sender details directly in this step. Once set, these values apply to all emails from this step and do not sync with the preset. * **From preset**: Use sender details saved in the selected email preset. If you update the details in the preset later, the change applies automatically. If any part of the preset’s sender details is missing, the step fills it in from your [default sender details](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#add-default-sender-details). If those are missing too, the [pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) flags it before you can launch the journey. Example You have a preset with the sender `news@company.com`. * If you select **From preset** in the step and later change the preset sender to `support@company.com`, the step automatically uses `support@company.com`. * If you select **Specify manually** and set `promo@company.com` in the step, the step keeps `promo@company.com` even if the preset changes. ##### To set sender details manually: 1. Enter the sender address in the **From Email** field (e.g., `marketing@testdomain.com`). The field suggests known mailbox names for each domain verified for your account. 2. Enter the sender name in the **From Name** field. 3. (Optional) Enable **Use this as the reply-to address to receive and track replies** if you want replies to go to the same address. ![Sender info settings in the Email element with Specify manually and From preset options](https://docs.pushwoosh.com/emails-send-email-34.webp) > **Note:** The email address must belong to a verified domain in your Pushwoosh project. See the [Email configuration guide](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) for details. ## Configure message delivery settings ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to control how often users receive email messages, helping to prevent over-messaging and reduce unsubscribes. Choose one of the following options: * **Use global frequency capping settings**. Apply the project-wide email limits set in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). *Example:* If the limit is 3 emails in 9 days, any additional emails during that period will be skipped. * **Send messages without global frequency capping**. Send the email regardless of how many messages the user has already received. Use this with caution to avoid overwhelming recipients. * **Use custom frequency capping**. Override global settings with a custom limit for this email step. If the user exceeds your custom limit, the email will not be sent, and they will move to the next step in the journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#customize-capping-for-specific-messages) Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings**\ Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate**\ Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate**\ Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) ### Ignore the sunset policy If your project has the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts) turned on, a marketing email skips contacts who haven’t opened or clicked your emails for the set period. To send this email to them too, turn on **Ignore sunset policy** in **Delivery settings**. The switch appears only for a **Marketing message**. ![Ignore sunset policy switch in the Email element delivery settings, with Inactive contacts won't receive this email and a Settings link](https://docs.pushwoosh.com/customer-journey-email-sunset-1.webp) To see how many recipients the sunset policy skipped, open the **Emails** tab in the journey statistics. Skipped recipients aren’t counted in **Sent** or **Delivered**. ## Enable Best time to send If you want each user to receive an email when they are most likely to open and engage with it, enable the **Best time to send** option. The optimal delivery time for each user is calculated based on their past behavior and the performance of previously sent emails. ![Best time to send option in Email element](https://docs.pushwoosh.com/journey-elements-README-10.webp) Note To use this option effectively, ensure that the default [PW_EmailOpen Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailopen) is enabled in your project. This will help collect enough engagement data for accurate time predictions. The accuracy of **Best time to send** depends on how much historical engagement data is available for each recipient. If there is not enough data on the user, they will receive an email at the **Default time** you specify according to their timezone. Important * The **Best time to send** is calculated for the next 24 hours after the user enters the journey point where this option is enabled. * If you’re using a [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) for the next step and the best time to send is later than the delay time, the user will wait until the best time, receive the message, and then proceed to the next step in the journey. * However, if the Time Delay is set to an exact date and time and the best time to send is later than that, the user will not receive the message and will exit the journey. ## Turn on BCC (Blind Carbon Copy) You can turn on the BCC (Blind Carbon Copy) option when sending emails in a customer journey. BCC allows you to send a copy of the email to an additional recipient without revealing their address to the main recipient(s). This feature allows you to: * Keep a record of customer interactions in your CRM system for better customer service. * Ensure internal teams (e.g. compliance and sales managers) receive copies for reference. Important note Activating the BCC option will double the count of sent emails, potentially leading to a higher monthly subscription cost. ##### How to enable BCC Note To use the BCC feature, please [contact our customer support team](https://help.pushwoosh.com/hc/en-us/requests/new). 1. In the Email element, toggle the **Send BCC** switch. 2. Enter the email addresses you want to receive the BCC copy. You can add multiple addresses. 3. Click **Apply**. ![Send BCC option in Email element](https://docs.pushwoosh.com/journey-elements-README-16.webp) ## Use vouchers You can personalize email messages by attaching a unique voucher code from a predefined pool. This is useful for promotions, discounts, and loyalty campaigns. Before starting, ensure your email content includes the `{{voucher}}` placeholder where the code should appear. To include a voucher in the email: 1. Toggle **Use Vouchers** to ON. 2. In the **Voucher Pool** field, select the pool containing your available voucher codes. The pool must be created in advance. 3. (Optional) In the **Assign Tag** field, specify a tag to be applied to users who receive a voucher. This can help with segmentation and reporting. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) ## Split flow depending on whether this message is opened or ignored You can split the remaining journey flow based on whether the email is opened or ignored. For example, it might be helpful to try reaching out to users via pushes or In-Apps or send one more email delivering more value. ![Split flow based on email opens in the journey](https://docs.pushwoosh.com/journey-elements-README-17.webp) Set the period to wait after the email is sent – after that period, all users who open the email will go to the **Opened** journey branch, and others will pass through the **Not opened** branch. Opens by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#opens-and-clicks-from-mail-scanners) don’t count, so a recipient whose email only a scanner opened goes to **Not opened**. Waiting period can be set to up to 7 days. ![Set waiting period for opened and not opened branches](https://docs.pushwoosh.com/journey-elements-README-18.webp) --- # Facebook Messenger **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/ Send Facebook Messenger messages in Customer Journey with presets or plain text, message tags, and personalized voucher codes. --- The **Facebook Messenger element** allows you to send Facebook Messenger messages to users as part of your Customer Journey. Before you start Connect your Facebook Page in **Settings → Configure platforms → Facebook Messenger** before using this element. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/). Otherwise, sending Facebook Messenger messages will not be available. In Customer Journey Builder, add the **Facebook Messenger** element from the channel elements, connect it after your Entry, then open the element to configure the settings below. ## Entry triggers When someone interacts with your connected Facebook Page, Pushwoosh logs events you can use to start a journey: * **User sent a message** (`PW_FBMessengerReceived`): use when the journey should start after any inbound text from the user. * **User tapped a menu, quick reply, or opened a referral link** (`PW_FBMessengerStarted`): this can fire more than once for the same person. Build logic around that specific action, not around first contact only. In the journey, add a **Trigger-based Entry**, open its settings, and select one of these events. [Learn how to configure Entry elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) ## Select message content Choose how to set the message content: * **Preset**: select a Messenger preset. Messenger presets are copies of [rich messages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/), so they can carry buttons, media, and cards. * **Custom content**: enter plain text in **Message Text**. ![Facebook Messenger element with message text, message tag, and Use Vouchers toggle](https://docs.pushwoosh.com/journey-elements-facebook-messenger-1.webp) ### Branch on reply buttons When the selected preset has quick reply buttons, the element gets a branch for each button, labeled with the button text, plus a **Not match** branch. * The user waits up to 24 hours for a tap. A tap sends the user down that button’s branch. If there’s no tap within 24 hours, the user goes to **Not match**. * Only quick reply buttons get branches. A button that opens a link or calls a phone number doesn’t. ## Branch when the provider rejects the message To send users whose Messenger message the provider rejected to a separate branch, for example to try another channel: 1. In the element settings, turn on **Branch when the provider rejects the message**. 2. Save the element. 3. On the canvas, connect the **Rejected by provider** branch to the next element. A user goes to **Rejected by provider** when Messenger refuses the message at sending, for example outside the 24-hour window without a message tag. Errors that Pushwoosh retries don’t lead to this branch. * **With quick reply buttons:** a rejected message goes to **Rejected by provider** at once. Other users wait for a tap. * **Otherwise:** the element holds the user for up to 1 hour, and the canvas shows **Wait for refusal: 1 hour**. If the provider doesn’t reject the message within that time, the user continues through **Sent**. Without a connected **Rejected by provider** arrow, a user whose message was rejected follows the element’s other branches. Turning the setting off removes the branch and its arrow. ## Use a message tag outside the 24-hour window If a user messaged your Page, you can respond with custom content within the 24-hour window that message opens. Leave **Message tag** set to **No tag** when you are sending inside that window. Outside that window, Meta requires a **Message tag** matching one of its approved use cases: * **CONFIRMED_EVENT_UPDATE**: a reminder or update about an event the recipient is scheduled to attend. * **POST_PURCHASE_UPDATE**: an update related to a purchase the recipient already made. * **ACCOUNT_UPDATE**: a non-recurring update about a change to the recipient’s account. * **HUMAN_AGENT**: a message from a human agent responding to the recipient. Choose a tag that matches the real reason for the message. Meta can reject sends that use the wrong tag. Important Pushwoosh does not independently track whether the 24-hour window is open. If the window is closed and no message tag is set, Meta rejects the send and Pushwoosh surfaces that error back to you. ## Use vouchers You can personalize Facebook Messenger messages by attaching a voucher code from a predefined pool. This is useful for promotions, discounts, and loyalty incentives. For this, include the placeholder `{{voucher}}` in your message text. Add the voucher pool in advance. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) To include a voucher in your Facebook Messenger message: 1. Toggle **Use Vouchers** to ON in the Facebook Messenger element. 2. In the **Voucher Pool** field, select the pool containing your available voucher codes. 3. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This tag can be used for segmentation or reporting. ![Facebook Messenger element Voucher Pool and Assign Tag fields shown after enabling Use Vouchers](https://docs.pushwoosh.com/journey-elements-facebook-messenger-2.webp) ## Limitations * **Custom content** is plain text only. Buttons, media, and cards need a preset. * You can’t confirm that one specific outbound Messenger message was read. Meta only reports that the conversation was read up to a certain time, not per message. * The channel only delivers once Meta has approved your Meta App’s Messenger permissions for the connected Page. After configuring the Facebook Messenger element, click **Apply** to save your settings. Launch the journey when the rest of the canvas is ready. Sending a single broadcast instead? This element covers Facebook Messenger inside a Customer Journey. For a one-time broadcast send outside a journey, use [Facebook Messenger in one-time messages](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/) instead. --- # In-app **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/ Deliver rich in-app messages during user journeys. Trigger personalized content based on behavior to drive engagement and retention. --- Note **Requirements** For Android apps, the OS versions 6.0 and later are supported. To show an [in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) message in a journey, add the **In-App** element next to the step that should trigger it. Then choose an in-app page to display. Click the **In-App** element to open its settings. Configure the following: ## Select template and set expiration Choose the in-app to show and set how long it stays available: 1. In **Media Source**, select an in-app template. 2. Set **In-app expiration**: how many days the message stays available, from 1 to 7. The default is 7 days. ![In-App element with Welcome Newcomers in Media Source, an arrow on the Sort: Newest first button, In-app expiration of 7 days, and Show in-app options](https://docs.pushwoosh.com/in-apps-sending-in-apps-14.webp) * **Template order.** The newest templates are at the top of the list. To change the order, click the sort button above the list. The order you pick also applies to the other in-app dropdowns. * **Older elements.** If an In-App element was saved with a longer expiration, it opens with 7 days. Personalize with Liquid To personalize the in-app with the user’s Tags and the attributes of the event that shows it, write [Liquid in the in-app content](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/). Liquid in in-apps is off by default. To enable it for your account, contact your Customer Success Manager or support. ## Choose when to show the in-app In **Show in-app**, choose when the user sees the message. **On next app launch** is selected by default. * **On next app launch**: when the user reaches the In-App element, the message is shown right away if the app is open. If the app is closed, it is shown the next time the user opens the app. * **On event**: the message waits for the event you select and is shown in the same session, right after the user triggers it. Use it to show an in-app message at the moment of an action, for example when the user opens the cart. ### Set up the On event mode To show the in-app message when the user triggers an event: 1. In **Show in-app**, select **On event**. 2. Select the event that shows the in-app message. 3. Optional: click **Add condition** to filter the event by its attributes. For the available operators, see [Tag operators by type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#tag-operators-by-type). 4. Click **Save**. The element saves only when an event is selected and its conditions are complete. #### How On event mode works In **On event** mode: * The in-app message reacts only to events that happen after the user reaches the In-App element. * If the event doesn’t happen within the **In-app expiration** period, the in-app message isn’t shown. * To check which event a running journey waits for, open the In-App element details. The event and its conditions are listed under **Shown on event**. ## Set message type Choose **Marketing message** or **Transactional message**. Until you choose one, the element shows the **Message type not selected** warning, and a journey that reaches it from a [Trigger-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) or [API-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/) can’t be launched: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: frequency capping and the silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. Note If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is already set and marked **Default**. ![In-app element with Marketing message and Transactional message options](https://docs.pushwoosh.com/in-apps-sending-in-apps-15.webp) [Learn more about the difference between marketing and transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive in-app messages, preventing over-messaging and reducing churn. In the In-App element settings, choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 messages in 9 days, additional messages exceeding this limit will be skipped. * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other in-app messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). --- # Issue wallet pass **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/ Issue an Apple or Google Wallet pass to a user in a journey, then deliver its install link with a later Push or Email step. --- Use the **Issue wallet pass** element to give users a pass for Apple Wallet or Google Wallet, such as a loyalty card, a ticket, or a coupon. The element creates a personal pass for each user from a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/). Note This element only creates the pass. To send it, add a **Push** or **Email** element after it. See [Send the pass link](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/#send-the-pass-link). ## Use cases * **Boarding pass after check-in:** when a user checks in, issue a boarding pass with their seat and gate, and send it by push before departure. * **Loyalty card on signup:** when someone joins your rewards program, issue a card with their starting points balance and add it to the welcome email. See [How to issue a wallet pass on signup](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-signup/) for the full walkthrough. * **Event ticket after purchase:** after a purchase, issue a ticket with the seat and row, and include it in the order confirmation. * **Pass on store geozone entry:** when a shopper enters a store’s geozone, issue a pass and push the pass link right away. See [How to issue a wallet pass on geozone entry](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-geozone-entry/) for the full walkthrough. ## Prerequisites Before you add this element: * **Wallet Pass Campaign:** create at least one [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/) for the application. **Issue wallet pass** can only issue passes from an existing campaign. You design the pass in the campaign, not in this element. * **Apple Wallet:** complete [Apple Wallet pass setup](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) for the application. See also [Getting started with Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/). * **Google Wallet:** configure [Google Wallet for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) for the application. * **Push delivery:** to send the pass link in a **Push** element, set up [iOS push notifications](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/) and/or [Android push notifications](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/) for the application. ## Add the element You’ll find **Issue wallet pass** under **Channels** in the journey builder. To design the pass itself, see [Wallet Pass Campaigns](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/). 1. Drag **Issue wallet pass** onto the canvas at the point where users should get the pass. 2. Double-click the element to open its settings. 3. Enter a recognizable name in **Step Name**, for example `Loyalty card`. You’ll use this name later when you insert the pass link into a Push or Email element. 4. In **Apple Wallet campaign** and **Google Wallet campaign**, select a campaign for either platform, or both. Leave a dropdown empty to skip that platform. ![Apple Wallet campaign set to Coffee discount and Google Wallet campaign left empty](https://docs.pushwoosh.com/journey-elements-issue-wallet-pass-1.webp) Each dropdown accepts one campaign and shows only this application’s campaigns for that platform, including Drafts. Archived campaigns aren’t listed. A Draft campaign issues passes just like an Active one, so you don’t need to activate it first. Tip Selecting both platforms lets the same step cover iPhone and Android users at once. Each platform issues its own pass for the same user. ### Fill in personal fields Personal fields are the parts of the pass that can differ from one user to another. Here you choose where each field gets its value. Under **Personal fields on the pass**, you see the fields that need a value for the campaign you selected. These are the same fields you fill in the campaign’s [**Issue pass**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/) dialog. If you select another campaign, the list changes to match. Fields that fill in automatically aren’t listed: * **Tag placeholder:** a field like `{Seat}` is filled from the user’s tag with the same name. * **Shared fields:** the same value on every pass, set once in the campaign itself. For each field, choose where its value comes from: * **Static value:** the same value on every pass this element issues. For example, `SUMMER2026` in the **OFFER** field. ![Personal fields on the pass with OFFER set to Static value and VALUE typed as SUMMER2026](https://docs.pushwoosh.com/journey-elements-issue-wallet-pass-4.webp) * **An event from the journey:** a value from an event earlier in the journey, so each user gets their own value. The dropdown lists the events of the journey: an entry or wait step shows as the step name and the event name, and an API-based entry, Webhook, or Issue wallet pass step shows as its step name only. Choose the event, then the **Attribute**, for example `offer_code` from the entry event. ![Personal fields on the pass with OFFER set to a Trigger entry event and Attribute set to offer_code](https://docs.pushwoosh.com/journey-elements-issue-wallet-pass-3.webp) With campaigns selected for both platforms, the list shows fields from both. A field that exists in only one campaign is filled only on that platform’s pass. Note If the event never fired for a user or the attribute was never set on that event, the pass uses the field’s default value from the campaign. The user isn’t dropped. See [Set shared and personal fields](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/#set-shared-and-personal-fields). When you’re done, click **Apply** to save the element settings. ## What happens at this step | Situation | What happens | | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The pass is issued | The user moves on to the next element. | | The user already has a pass from this campaign, for example after a [loop](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/loop/) in the journey | The element reuses that pass instead of creating a new one, and the user moves on. Each user has at most one pass per campaign. | | The user has no [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) | The user drops out of the journey. By default, the SDK uses the device HWID as the User ID, so this mostly happens when a user enters through the API or with an event that has no User ID. | | The pass can’t be issued, for example because the campaign was deleted or archived after you added the element | The user drops out of the journey. | | The wallet service doesn’t respond in time | The user drops out of the journey with the **Internal error** reason. | A dropped user isn’t retried automatically. If both platforms are configured, Apple is attempted first. If Apple fails, the user drops before Google is attempted. If Apple succeeds and Google fails, the user still drops, and the Apple pass stays issued. ## Send the pass link After **Issue wallet pass**, add a push or email with a link the user taps to add the pass to their wallet. The link is available only for the platforms you selected in the element. Use `apple_install_link` for Apple Wallet and `google_save_link` for Google Wallet. 1. Add a [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) or [**Email**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) element after **Issue wallet pass**. 2. In the message, type the placeholder with vertical bars, for example `{apple_install_link|regular|}`. Don’t use a plain `{apple_install_link}`. `regular` is a required format modifier that inserts the value unmodified, so leave it as is. See [Dynamic Content or Liquid Templates](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/) for the full syntax. In **Email**, you can use the placeholder in the subject line too, not just the message body. 3. Turn on **Personalise message with event attributes**. 4. In the table, check **Overwrite placeholder** next to your placeholder. This unlocks the **Source event** and **Dynamic attribute** dropdowns. 5. In **Source event**, choose your **Issue wallet pass** step by the **Step Name** you gave it, for example `Loyalty card`. 6. In **Dynamic attribute**, choose `apple_install_link` or `google_save_link` to match the placeholder you used. ![Personalise message with event attributes table mapping apple_install_link to the Loyalty card step](https://docs.pushwoosh.com/journey-elements-issue-wallet-pass-11.webp) Note In **Push**, if you edit the message text, select the preset again so the new placeholder appears in the table. Why can't some users open the link? The element issues a pass only for the platforms you configured, not for the user’s actual device. If you configure only Apple Wallet, Android users still get the message, but with a link they can’t use. If you configured both platforms, include both links in the message (“Add to Apple Wallet” and “Save to Google Wallet”), or split the journey by platform before this message and send each link separately. ## Track results The element’s panel shows entries and drop-offs. For a user who was dropped, it also shows the reason: * [No User ID](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#no-user-id) * [Wallet pass wasn’t issued](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#wallet-pass-wasnt-issued) * [Internal error](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#internal-error), when the wallet service doesn’t respond in time Users who pass through the element without a drop have a pass issued or reused. To check that the link reached users, see the [statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#push-notifications-and-email-messages) of the next [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) or [**Email**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) element. ## Serial numbers Each issued pass has a serial number: `apple_serial_number` or `google_serial_number`. Use it to: * Log the pass in your CRM or support tool with a [**Data to app**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) or [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) element. * Find the pass on the campaign’s **Passes** tab. * [Attach the pass to a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/#attach-an-existing-pass-to-a-campaign). --- # Kakao **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/kakao/ Send approved Kakao messages in customer journeys using Kakao templates with dynamic variables and optional vouchers. --- The Kakao element in Pushwoosh Customer Journey lets you send Kakao messages to users using pre-approved Kakao templates. Note Kakao messages can be sent only using [approved templates](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/). Custom free-form messages are not supported. ## Add the Kakao element to a journey To send a Kakao message, drag the **Kakao** element onto the journey canvas and connect it to the desired step in your flow. Once added, click the element to open its configuration panel. ## Select message content In the **Content** dropdown, select one of your Kakao templates. To change the order of templates in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every Kakao template dropdown, in one-time messages and journeys alike. Note Only templates with APR (Approved) status are available in the dropdown. If you don’t see a template, check [Kakao templates](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) in **Content > Messenger presets** on the **Kakao** tab to make sure it has been approved and is not in REQ (Requested) or REJ (Rejected) status. ![Kakao template selection in Customer Journey element settings](https://docs.pushwoosh.com/kakao-send-kakao-messages-2.webp) After selecting a template, Pushwoosh shows a preview of the message text. ## Personalize the message with variables If your template includes placeholders, you must map them before sending. Click inside the message where the variable should appear and insert a variable using the variable picker. For each variable, you can choose: * **Variable type**: * User property: data from the user profile (for example, `firstName`) * Event property: data from the event that triggered the journey * **Text case** (optional), such as CapitalizeFirst * **Fallback text** (optional), which is shown if the value is missing ![Variable selector interface for personalizing Kakao messages in Customer Journey](https://docs.pushwoosh.com/kakao-send-kakao-messages-3.webp) *Example message:* ```plaintext Hey, #{firstName}! Your order is ready. ``` If `firstName` is empty, the fallback text (for example, `there`) will be used instead. ## Send to all user devices Enable **Send to all user devices (from UserID)** if you want the message to be delivered to all devices associated with the same user ID. When disabled, the message is sent only to the device that triggered the journey. [Learn more about User IDs](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) ## Use vouchers You can personalize Kakao messages by including a unique voucher code from a predefined pool. This is ideal for delivering promotions, discounts, or loyalty incentives through one-on-one communication. Add a voucher placeholder in Kakao format so the code is rendered in the text, for example `#{voucher}`. Next, configure where the voucher comes from and how it is assigned: 1. Toggle **Vouchers** to ON in the Kakao message step. 2. In **Voucher Pool**, select the pool that contains your voucher codes. Make sure the pool is created in advance. 3. (Optional) Use **Assign Tag** to apply a tag to users who receive a voucher. This helps with segmentation and reporting. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) After configuring the Kakao element, click **Save** to apply the settings. --- # LINE **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/line/ Send personalized LINE messages in customer journeys with presets, vouchers, and delivery tracking to boost user engagement and conversions. --- The **LINE element** allows you to send LINE messages to users as part of your Customer Journey. You can choose between using a message preset for advanced formatting or entering simple free-form content. To configure the LINE element, enter a step name and choose how you want to define the message content: * **Preset**\ Select from the LINE message presets you’ve created under **Content > LINE Presets**. [Learn more about creating LINE presets](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets) * **Custom content**\ Enter plain-text content directly into the **Custom content** field. ![LINE element Preset field with Weekend flash sale selected and an arrow on the Newest first sort button](https://docs.pushwoosh.com/line-send-line-messages-1.webp) To change the order of presets in the dropdown, click the sort button above it. The dropdown is sorted by **Newest first** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every LINE preset dropdown, in one-time messages and journeys alike. > **Note:** When using custom content, no additional settings are available. Users will only receive the plain text you enter. To access advanced configuration options, use a preset. ## Use vouchers You can personalize LINE messages by including a unique voucher code from a predefined pool. This is ideal for delivering promotions, discounts, or loyalty incentives through one-on-one communication. To use vouchers in your LINE message: 1. Ensure your LINE message template includes the placeholder `{{voucher}}`. 2. In the LINE message step, toggle **Vouchers** to ON. 3. Select the appropriate **Voucher Pool** that contains your voucher codes. Make sure the pool is created in advance. 4. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This helps with segmentation and reporting. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) ## Branch on reply buttons When the selected preset has quick reply buttons, for example a LINE copy of a [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/), the element gets a branch for each button, labeled with the button text, plus a **Not match** branch. * The user waits up to 24 hours for a tap. A tap sends the user down that button’s branch. If there’s no tap within 24 hours, the user goes to **Not match**. * The **Split flow depending on whether this message is delivered or not** toggle isn’t available, because the buttons branch the element. * Only quick reply buttons get branches. A button that opens a link or calls a phone number doesn’t. If you switch to a preset with different buttons and save the element, its arrows are removed. Reconnect the branches on the canvas. ## Branch when the provider rejects the message To send users whose LINE message the provider rejected to a separate branch, for example to try another channel: 1. In the element settings, turn on **Branch when the provider rejects the message**. 2. Save the element. 3. On the canvas, connect the **Rejected by provider** branch to the next element. A user goes to **Rejected by provider** when LINE refuses the message at sending. Errors that Pushwoosh retries don’t lead to this branch. * **With quick reply buttons:** a rejected message goes to **Rejected by provider** at once. Other users wait for a tap. * **With the delivery split:** the element waits for the time set there. * **Otherwise:** the element holds the user for up to 1 hour, and the canvas shows **Wait for refusal: 1 hour**. If the provider doesn’t reject the message within that time, the user continues through **Sent**. Without a connected **Rejected by provider** arrow, a user whose message was rejected follows the element’s other branches. Turning the setting off removes the branch and its arrow. ## Delivery-based flow control (optional) Use the **Split flow depending on whether this message is delivered or not** toggle to define how the journey should proceed based on delivery status: * **Enabled**: The journey splits into two paths—one for successful delivery and one for delivery failure. * **Disabled**: The journey continues along a single path, regardless of delivery outcome. ![Split flow based on LINE message delivery status](https://docs.pushwoosh.com/line-send-line-messages-2.webp) Click **Save** to apply your settings and return to the journey canvas. After configuring the LINE element, click **Save** to apply your settings. --- # Live Activity **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/ Use the Live Activity element to start, update, or end an iOS lock-screen card, an Android 16 notification, or both, in a journey. --- A Live Activity is a card that updates in real time so the user sees progress without opening the app (flight status, delivery, ride, and similar). On iOS it’s a small card on the lock screen and Dynamic Island. On Android 16 and later, it’s the same kind of card shown as an ongoing notification with a progress bar. Use the **Live Activity** element in a journey to start, update, or end that card, on iOS, Android, or both. Each element does one action: * **Start:** create the card. * **Update:** change an existing card. * **End:** close the card. To change or close the same card later, add another **Live Activity** element and point it back at the one that created the card with **Card created by**. ## Example use cases Use this element whenever the user should see a status that keeps changing, without opening the app. * **Flight status:** Show the card after check-in. Keep gate, status, and time up to date during the flight. Remove the card after landing. * **Food delivery:** Show the card when the order is placed. Keep courier name, ETA, and distance up to date on the way. Remove the card on delivery. * **Ride-hailing:** Show the card when the ride is requested. Keep driver, ETA, and plate number up to date while the driver approaches. Remove the card when the ride completes. * **Order or appointment:** Show the card when the order or booking is confirmed. Keep status up to date as it progresses. Remove the card when it is fulfilled or the visit is over. * **Live event:** Show the card when the event begins. Keep score, period, or schedule up to date while it runs. Remove the card when the event finishes. Tip For full journeys, see [How to show flight status on the lock screen](https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-flight-status-with-live-activity/) and [How to show delivery progress on the lock screen](https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-deliveries-with-live-activity/). ## Prerequisites Before you set up this element, check what each platform needs. **For the iOS card:** * **iOS Live Activity support:** your app must support Live Activities. See the [iOS SDK Live Activities guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/). * **A published widget schema:** Ask your development team to publish the schema that matches the Live Activity type in the app under [Applications → Configure → Live Activity schemas](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-live-activity-schemas-configuration/). They can also [publish via the API](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#create). For what belongs in a schema, see [Writing a schema](https://docs.pushwoosh.com/developer/api-reference/live-activity-schemas-api/#writing-a-schema). * **Widget in the list:** After the schema is published, select it under [**Widget**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/#set-up-the-element) in this element. If the **Widget** list is empty, the schema is not published yet. **For the Android notification:** * **Android Live Updates support:** your app needs SDK 6.11+ and the `pushwoosh-liveupdates` module. No schema is needed for Android. Ask your Android developer to confirm the module is in the build. ## Set up the element 1. Drag the **Live Activity** element onto the canvas. ![Live Activity entry highlighted in the channel element list](https://docs.pushwoosh.com/live-activity-8.webp) 2. Double-click the element to open its settings. 3. Enter a name in **Step name**. 4. In **Action**, choose one of the following: * **Start:** create the Live Activity card. * **Update:** change the content of an existing card. * **End:** close the card. 5. Under **Platforms**, turn on **iOS Live Activity**, **Android Live Updates**, or both. At least one platform must stay on, so you can’t turn off the last one. On **Update** and **End**, **Platforms** shows the platforms from the linked **Start** element and is read-only. ![Action set to Start, with both iOS Live Activity and Android Live Updates turned on under Platforms](https://docs.pushwoosh.com/live-activity-9.webp) 6. On **Start** only, set the card key so later **Update** and **End** steps can find this card: * In **Card key: event**, select the event that identifies the card (for example the journey entry event). * In **Card key: attribute**, select the attribute that makes the key unique per traveler. This is required once you set **Card key: event**. Leaving it unset makes the event selection a no-op, the same as leaving both fields empty: one card per traveler, addressed by the default user ID. ![Card key: event and Card key: attribute fields on a Start element](https://docs.pushwoosh.com/live-activity-16.webp) ### Link Update and End to the right card When **Action** is **Update** or **End**, use **Card created by** to point to the exact **Start** element that created this card. Otherwise **Update** or **End** won’t reach it. 1. In **Card created by**, select the **Step name** of that Start element (for example `Order card start`). After you pick **Card created by**, **Card key (from the start element)** shows the **Card key: event** and **Card key: attribute** values from that Start. It is read-only and confirms which card this points to. ![Update element showing Card created by and the read-only Card key inherited from the linked Start](https://docs.pushwoosh.com/live-activity-7.webp) Note Example: **Start** is named `Order card start`. On **Update** and **End**, choose `Order card start` in **Card created by**. Both steps then change or close that same order card. ## Choose the card language **Card language** applies to both the iOS card and the Android notification. Set **Card language** to `default` or a specific language code. Content under `default` is the fallback for any language you don’t fill in separately. ## Set up the iOS card Skip this section if only **Android Live Updates** is on. ### Choose the widget and schema version 1. In **Widget**, select the published Live Activity type for this card. The content fields below come from that choice. On **Update** or **End**, **Widget** is read-only, inherited from the **Card created by** element. 2. In **Schema version**, select which published version of that widget’s schema to use. The **Card content** fields come from this version. On **Update** and **End**, **Schema version** stays a picker: you can pick a different published version of the same inherited widget than the linked **Start** used. ![Widget and Schema version fields on a Start element](https://docs.pushwoosh.com/live-activity-15.webp) ### Set the card’s fixed attributes (Start only) On **Start**, under **Card attributes**, add the fields that stay fixed for the card’s whole life, set once and never changed again, like a flight number or an order ID. These are separate from the **Card content** fields below. Those values can change on **Update**. Ask your iOS developer for the exact **Field name** list. Those names stay fixed for the card’s whole life (the app’s `ActivityAttributes`). Do not use the changing **Card content** names (the app’s `ContentState`). 1. Click **Add attribute**. 2. Set **Field name** and **Value** for each attribute you need. **Update** and **End** don’t set attributes. Whatever **Start** set for this card stays fixed. Caution Card attributes are sent exactly as your app’s `ActivityAttributes` type expects, field for field. Get a name wrong, or leave them empty, and the device’s decoder rejects the whole payload. In one live test, a **Start** with `flightNumber` opened the card, and a **Start** with no attributes delivered only the envelope, with nothing to show on the lock screen. ### Fill in the card content Under **Card content**, type a literal value or a personalization placeholder into each field. One field appears per property in the selected schema version. ![Card language set to default, and gate, status, and estimatedTime Card content fields filled in for a Start element](https://docs.pushwoosh.com/live-activity-3.webp) ### Prefill on Update and End On **Update** or **End**, if **Card content** for the current **Card language** is empty (including a language you just added), Pushwoosh prefills the fields from the linked **Start** element when you open the settings: * Same language as **Start**, if that language has content. * Otherwise **Start**’s `default` content. * If **Start** has neither, leave the fields empty and fill them yourself. Prefill values stay editable. Click **Apply** only when you want to keep the edits. Opening the element alone does not change a running journey. Fields you leave empty on **Update** or **End** aren’t sent. What the card then shows in those fields depends on your app: it may keep the previous value, clear it, or do something else. Ask your developers how your app handles this. On **End**, **Card content** is optional. A field you fill in becomes the last value shown before the card closes. ### Set delivery priority and timing 1. In **Delivery priority**, pick when iOS should deliver this update: * **Immediate:** iOS delivers right away and can wake the phone (and play the sound, if you set one). * **Quiet:** iOS may deliver later with other updates and does not wake the phone right away. * **Default (batched):** iOS uses its own default batched delivery and does not wake the phone right away. 2. In **Sound**, select a sound from the list. Your development team adds sound files to the iOS app bundle. See [Custom push sound](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#custom-push-sound). Sound only plays together with **Alert title** or **Alert text**, same as the banner. 3. Depending on the **Action** you set for this element (**Start**, **Update**, or **End**), fill one of the following: * **Start** or **Update:** set **Stale after, min** to how many minutes the data on the card should stay looking fresh. When that time ends, iOS dims the numbers as outdated. The card stays on the lock screen. To keep the numbers looking current, send another **Update** before the time ends. * **End:** set **Dismiss after, min** to how long the closed card stays on the lock screen before iOS removes it. Leave it at `0` and the card keeps showing its final **Card content** until iOS retires it on its own, within up to 4 hours. 4. Optionally set **Relevance score** to a number from 1 to 100. When a person has more than one active Live Activity from your app at once, iOS shows the one with the higher score first. Leave it at `0` to not set a preference. Pushwoosh doesn’t send a `0` score to Apple at all. See [Multiple activities per device](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/#multiple-activities-per-device) for the full picture. ![Delivery priority, Sound, Stale after, and Relevance score fields on a Start element](https://docs.pushwoosh.com/live-activity-5.webp) ## Fill in the Android notification Fill in the Android notification’s title, text, progress bar, and header time. This section appears only when **Android Live Updates** is on. It uses the same [**Card language**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/#choose-the-card-language) as the iOS card. 1. Set **Notification title** for every language you fill in for Android. On **Start** and **Update**, the journey can’t run until each of those languages has a title. A language without a title shows a reminder in the form. 2. Set **Notification text**. ![Android Live Updates section heading with hint text, and Notification title and text fields filled in](https://docs.pushwoosh.com/live-activity-12.webp) 3. Set up the progress bar: * **Progress:** type a number or a placeholder in the `{name}` form (optionally `{name|format}` or `{name|format|default}`) for where the bar should sit, in the same units as the segment lengths. * **Segments:** click **Add segment** for each colored piece of the bar, and set a hex **Color** (`#RRGGBB` or `#AARRGGBB`) and a **Length** for each. Segment lengths add up to the full bar. * **Animate the bar without a known end:** turn on to show a moving bar instead of the **Progress** value. * **Hide the progress bar:** turn on to show the card with no bar. ![Progress set to 65, Animate the bar and Hide the progress bar toggles off, and two Segments filled in](https://docs.pushwoosh.com/live-activity-13.webp) 4. Set the header time: * **Header time:** type a Unix timestamp in seconds (not milliseconds), or a placeholder, for the moment the card’s header clock should show. For example, `1735689600` means 2025-01-01 00:00 UTC. If both this and **Header time after, min** are set, **Header time** is used. * **Header time after, min:** set how many minutes after send the header time should show. * **Run the header time as a timer:** turn on to show **Header time** as a running clock instead of a fixed value. This reveals **Count down to the header time**. * **Count down to the header time:** turn it on to count down toward **Header time** instead of counting up from send. * **Hide the header time:** turn on to show the card without the header time. ![Header time empty, Header time after set to 8 minutes, Run the header time as a timer on, Count down to the header time and Hide the header time off](https://docs.pushwoosh.com/live-activity-14.webp) Any field above can carry a placeholder, resolved the same way as the iOS **Card content** fields: from the journey event or [personalized with an event attribute](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/#personalize-the-content). Some Android devices won't show the notification * **Android 15 or earlier, or an app without the `pushwoosh-liveupdates` module:** Pushwoosh still sends the update, but nothing appears on the device, and no regular push is shown instead. * **Huawei and Baidu devices:** they don’t get Android Live Updates. Updates go only to devices registered through Firebase Cloud Messaging (FCM). Tapping the notification opens the app, the same as a regular push. ## Set the alert banner This section applies only when **iOS Live Activity** is on. If only **Android Live Updates** is on, these fields are hidden and nothing is sent. For all three actions (**Start**, **Update**, and **End**): 1. In **Alert title**, set the banner title shown on the lock screen. 2. In **Alert text**, set the banner text. Caution If both **Alert title** and **Alert text** are empty, the card still starts, updates, or ends, but the lock screen shows no banner and plays no sound. Fill in at least one of the two when this action should surface on a locked screen. ![Alert title and Alert text fields filled in for a Start element](https://docs.pushwoosh.com/live-activity-4.webp) ## Choose which device gets the card Addressing is set once, on **Start**. Leave both switches off to send the card to the device the traveler entered the journey on. Turning one switch on turns the other off: * **Send to all devices of this user:** send to every device registered under that traveler’s User ID, not just the one they entered on. * **Send to the last active device only:** send to the single device that User ID used most recently, instead of every device or the entry device. Tip Turn on **Send to the last active device only** when the same person might be watching on a different iPhone than the one that entered the journey. On **Update** and **End**, check **Delivery (from the start element)**. It names the addressing mode from the linked **Start**. The update can only reach the same card, so it goes out the same way. Note Both switches lock as soon as the journey starts running, on **Start** too. To change addressing after that, pause the journey first. ## Personalize the content Use this when placeholders in **Alert title**, **Alert text**, **Card content**, or the Android **Notification title**, **Notification text**, **Progress**, or **Header time** should take values from the journey event or API-based entry instead of device tags. 1. Under **Overwrite personalization**, turn on **Personalise message with event attributes**. 2. Check the **Overwrite placeholder** box next to each placeholder you want to remap. 3. Map that placeholder to an event attribute. ![Overwrite personalization block with the Personalise message with event attributes toggle enabled](https://docs.pushwoosh.com/live-activity-6.webp) ## Save the element Click **Apply** to save the element settings. **Apply** saves this element in the journey. It does not confirm that the card appeared on the device. After the journey is running, check **Total entries** and drop-offs on this step, and verify the card on a test iPhone, a test Android 16-and-later device, or both, depending on the platforms you turned on. ## Limitations * **Element statistics:** On this step, check **Total entries**, the **Delivery** row (addressing mode), and drop-offs (**No recipient for the card**, **Live Activity send failed**). Use **No recipient for the card** to see that messaging found no device for the enabled platforms under that mode, not that the device lacked a Live Activity token. This step does not report whether the device showed the card or whether the user opened it. * **Sound isn’t guaranteed on every update:** iOS rate-limits Live Activity alerts on its own. An identical update can play a sound one time and arrive silently the next. * **Many Start actions in a row during testing:** If you send about ten **Start** actions for the same person in a short time (for example while testing the journey), Apple may stop showing new cards and not return an error. In the journey the person can still look delivered. Leave a gap between test runs. --- # Multi-Armed Bandit **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/multi-armed-bandit/ Automatically send the best-performing push variant in journeys based on real-time opens or conversion events, without A/B tests --- The **Multi-Armed Bandit** element sends a push notification to users, just like the [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element. Instead of a single preset, you provide several content variants. Optimization runs continuously on live data, without manually restarting the test or keeping a fixed traffic split. Pushwoosh shifts more traffic toward the variant that gets opened most or leads to the most conversions. Multi-Armed Bandit is an experimental feature * **Variants:** only existing push presets are supported. Email, SMS, or in-app content variants aren’t supported yet. * **Selection:** based on overall variant performance across all users, not on individual user preferences. Pushwoosh avoids sending the same variant to the same person twice in a row. Behavior and settings may change as this feature develops. Add the **Multi-Armed Bandit** element after the event or condition that should trigger it, then configure the element. ![Multi-Armed Bandit element added to a journey canvas after a trigger](https://docs.pushwoosh.com/journey-elements-smart-content-1.webp) ## Add content variants Tip To use Multi-Armed Bandit, first create two or more push presets in your account. [Learn more about creating push presets](https://docs.pushwoosh.com/product/content/push-presets/). You need at least two variants to save the element. 1. Double-click the **Multi-Armed Bandit** element to open its settings. 2. In **Content variants**, select at least two different push presets. 3. Click **Add variant** to include more presets. There is no upper limit. If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, this picker doesn’t show version status or disable unapproved presets, but every variant still needs to be **Approved**. Launching or resuming the Journey blocks on any variant that isn’t. ![Multi-Armed Bandit Content variants section with two selected push presets and Add variant button](https://docs.pushwoosh.com/journey-elements-smart-content-2.webp) ## Choose success metric In the **Reward** section, pick what counts as a win for a variant: * **Push opened:** a variant wins a send when the user opens that push. * **Event happened:** a variant wins a send when the user triggers an event after receiving that push, for example a purchase. Select the event in **Reward event**. The list shows events of the journey’s project. [Learn more about events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). With **Event happened**, Pushwoosh credits the event to the last push this element sent to the user before the event, if the event happens within that push’s reward window. If the user triggers the event several times within the window, that push still counts as one conversion. Set **Reward window (hours)** to define how long Pushwoosh waits after a send for the open or the event: | Reward | Default window | Allowed window | | ------------------ | -------------- | -------------------- | | **Push opened** | 2 hours | 1–72 hours | | **Event happened** | 24 hours | 1–168 hours (7 days) | You can’t save the element with a window outside the allowed range for the selected reward. In a new element, changing the reward switches the window to that reward’s default. In a saved element, the window keeps its value, so check that it suits the new reward. ![Multi-Armed Bandit Reward section with Event happened selected, CheckoutSuccess chosen as the reward event, and a 24-hour reward window](https://docs.pushwoosh.com/journey-elements-smart-content-3.webp) Pushwoosh compares variants only once the reward window has passed since the first sends. A longer window catches more late opens or events, but Multi-Armed Bandit also waits longer before it can learn from each send. Note If the user opens the push or triggers the event after the reward window closes, the open or the event still appears in your regular statistics. It doesn’t count toward Multi-Armed Bandit optimization: for learning, that send is treated as not converted. Pick a window long enough to capture the opens or events that matter. ## Personalize variants and add vouchers Note These settings apply to the whole Multi-Armed Bandit element, not to each variant individually. Whichever preset Multi-Armed Bandit ends up sending to a user, the same personalization rules and voucher pool fill in its placeholders. If only some of your variant presets include a placeholder or the `{{voucher}}` tag, the other variants don’t use it. You can’t set different event mappings or voucher pools per variant. ### Personalize with Dynamic Content Toggle **Personalise message with event attributes** to fill placeholders in your variant presets with values from an event, instead of device tags. Multi-Armed Bandit collects the placeholders used across all your selected variants into one list, so you map each placeholder once, no matter which preset ends up being sent. [Learn more about personalization in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/). ### Overwrite Liquid placeholders Toggle **Overwrite Liquid placeholders** and select an event to feed its attributes into Liquid tags in your variant presets that match by name. If you enable both Dynamic Content and Liquid personalization, Liquid takes priority. ### Use vouchers Toggle **Use Vouchers** to attach a unique code from a voucher pool to whichever variant gets sent. Create your variant presets with the `{{voucher}}` placeholder in advance, then select the **Voucher Pool** to pull codes from. Optionally, use **Assign Tag** to save the delivered code to a user tag. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/). ## Set message type Choose **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: frequency capping and the silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![Multi-Armed Bandit element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/journey-elements-smart-content-5.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Configure message delivery settings ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive push messages, preventing over-messaging and reducing churn. In the Multi-Armed Bandit element settings, choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 messages in 9 days, additional messages exceeding this limit will be skipped. * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other push messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) When you finish configuring the Multi-Armed Bandit element, click **Save**. ## How Multi-Armed Bandit picks a variant Multi-Armed Bandit doesn’t alternate between variants evenly or run a fixed 50/50 split like [A/B/n split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/). Instead, it continuously watches which variant gets opened more often, or leads to the reward event more often, and gradually sends that variant to a larger share of users. It still sends the other variants often enough to keep learning from real data. A few things to know about how it behaves: * It takes some time and volume for a clear leader to emerge. Early on, variants are sent close to evenly. * Pushwoosh avoids sending the same variant to the same user twice in a row, so people don’t get the exact same message repeatedly. * Selection is based on the overall performance of each variant across all users in the journey, not on an individual user’s preferences. Even after a variant is confirmed as the winner, Multi-Armed Bandit keeps running. Unlike an [A/B/n split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/), it doesn’t lock in the winner and route all traffic to it. The winning variant keeps getting the largest share of sends, while the others still receive a smaller share so Pushwoosh can keep learning. If performance shifts over time, Multi-Armed Bandit adapts and can move traffic to a new leader. ## Example scenario: Recovering an abandoned cart Use Multi-Armed Bandit when users add items to their cart but don’t complete checkout. The example below starts a journey on a **Cart abandoned** event and tests three push variants to bring users back. 1. **Prepare push presets.** Create three push presets in your account before you build the journey. [Learn more about creating push presets](https://docs.pushwoosh.com/product/content/push-presets/). For this abandoned-cart scenario, you might create presets like these: * **Cart reminder:** “Items are waiting in your cart” * **Cart urgency:** “Your cart expires in 1 hour” * **Cart discount:** “Get 10% off if you check out now” 2. **Create the journey.** In **Campaigns → Customer Journey Builder**, click **Create Campaign** and choose **Build a Journey from Scratch**. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element to the canvas. 3. **Set the trigger.** Create or enable a **Cart abandoned** event that fires when a user doesn’t complete checkout within a few hours. In the **Trigger-based entry** element, select this event as the journey trigger. [Learn more about events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). 4. **Add the Multi-Armed Bandit element.** Place the **Multi-Armed Bandit** element after the trigger, then open its settings. 5. **Add content variants.** In **Content variants**, select the three presets you created in step 1. 6. **Choose the success metric.** In **Reward**, select **Push opened**. Set **Reward window** to 2 hours. This window is long enough to catch opens from users who don’t check their phone right away, but short enough to keep learning fast. 7. **Finish setup and launch.** Configure message type and delivery settings, click **Save**, and activate the journey. ### After launch Multi-Armed Bandit may behave roughly like this. Actual traffic split and timing depend on send volume and how users respond to each variant. First, Pushwoosh sends all three variants close to evenly, since it doesn’t have enough data yet. As opens come in, it starts shifting more traffic toward whichever variant gets opened more. For example, if “Get 10% off if you check out now” consistently outperforms the other two, it might receive about 60% of sends while the other two split the rest. Pushwoosh keeps sending the lower-performing variants too. This keeps learning going and follows the rule that a user who was just sent one variant won’t receive that same variant again the next time they hit this step. ## Review results After you save the element and launch the journey, open the Multi-Armed Bandit element’s statistics panel to see how your variants are performing. ![Multi-Armed Bandit statistics panel with verdict banner and per-variant Sent, Opened, and Traffic metrics](https://docs.pushwoosh.com/journey-elements-smart-content-4.webp) A banner at the top shows the current status of the experiment: * **Not enough data yet:** Multi-Armed Bandit hasn’t collected enough sends to say anything meaningful. * **Still learning:** there’s data, but no variant is a clear, statistically significant leader yet. * **Significant leader:** one variant is confirmed to outperform the others. It’s marked with a **Winner** badge. For each variant, you’ll see: * **Sent:** how many times this variant was sent. * **Opened** (with **Push opened**) or **Converted** (with **Event happened**): the share of sends that got the reward within the reward window. * **Traffic:** the current share of new sends going to this variant. ## Multi-Armed Bandit vs. A/B/n split Both **Multi-Armed Bandit** and [**A/B/n split**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) optimize your messaging, but they work differently. Use the table below to decide which one fits your use case. | | **Multi-Armed Bandit** | **A/B/n Split** | | ----------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Mechanics** | Continuously shifts traffic toward the best-performing variant based on live opens or conversion events | Splits users into fixed percentage branches you define upfront. Branches can hold entirely different downstream flows | | **Variants** | At least two push presets required. No upper limit on variants | Up to four branches per test | | **Traffic split** | Starts close to even, then shifts automatically as data comes in | Fixed percentage you set, stays fixed until a winner is picked | | **Duration** | Runs continuously, with no defined end | Runs until statistical significance is reached or you stop it manually | | **Winner** | Never locks in: the winning variant gets more traffic, but the others still receive some | Locks in after a winner is picked: losing branches deactivate, and all new users route to the winner | | **Best for** | Ongoing content optimization for a single message step (push presets only, for now) | Testing structurally different flows or sequences when you want a one-time, locked-in decision | --- # Push **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/ Send personalized push notifications in journeys with dynamic content, vouchers, inbox saving, frequency limits, and flexible delivery timing --- Push element indicates a point to communicate to a customer with a push notification. To send a push message at the right moment in the journey, add the Push element after the event or condition that should trigger the message. ## Select or create push content Then, select a preset from your Pushwoosh account or create one. [Learn more about creating push presets](https://docs.pushwoosh.com/product/content/push-presets/). To change the order of presets, click the sort button next to **Preset**. The list shows the newest presets first until you pick another order. Your choice is saved in your browser and also applies to push preset dropdowns outside journeys, such as one-time pushes and geozones. If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, only **Approved** versions can be selected here. Other versions show their status and stay disabled. ![Push element settings with the Welcome push - registration preset selected, its preview, and an arrow pointing to the Newest first sort button next to Preset](https://docs.pushwoosh.com/journey-elements-channels-1.webp) Caution Please be aware that if any segmentation rules are saved in a Push Preset you choose, they won’t be applied to the journey. ### Send push stories You can select a [push stories](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/) preset in the Push element. Push stories render only on iOS devices with Pushwoosh iOS SDK 7.0.46 or later and a Notification Content Extension for stories. Recipients who don’t have that app update yet see a regular push with the title, subtitle, and message from the preset, without the Stories images. When you select a push stories preset, a banner in the element lists these requirements. Click **Setup guide** in the banner to open the [iOS push stories developer guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-stories/) and share it with your iOS team. ![Push element with a push stories preset selected and the info banner listing iOS SDK 7.0.46 and Notification Content Extension requirements, with the Setup guide button](https://docs.pushwoosh.com/journey-elements-push-stories-banner.webp) ## Set message type Choose **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: frequency capping and the silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. ![Push element with neither Marketing message nor Transactional message selected and the Message type not selected warning](https://docs.pushwoosh.com/journey-elements-push-14.webp) If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![Push element reached only from an Audience-based entry, with Marketing message shown as the Default](https://docs.pushwoosh.com/journey-elements-push-message-type-default.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Personalize messages with Dynamic Content and Liquid Templates For greater relevance of your messages to users in the journey, you can add personalized content based on users’ behavior. For example, when sending an Abandoned Cart push, add the product name for better conversion. That reminds users of what they wanted to purchase and makes your message more convincing. [Learn more about personalization of journey messages](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/) ## Use vouchers You can personalize push notifications by attaching a voucher code from a predefined pool. This is useful for promotions, discounts, and loyalty incentives.\ For this, create a push preset that includes the placeholder `{{voucher}}` in advance. To include a voucher: 1. Toggle **Vouchers** to ON. 2. In the **Voucher Pool** field, select the pool containing your available voucher codes. Make sure to add the pool of vouchers in advance. 3. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This tag can be used for segmentation or reporting. ![Enable the Use Vouchers option](https://docs.pushwoosh.com/content-vouchers-9.webp) [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) ## Save to inbox Toggle **Save to inbox** to keep a copy of this push in the app’s inbox. Users still get the push. They can open the same message later if they dismiss or miss the banner. 1. Toggle **Save to inbox**. 2. In **Remove from Inbox**, choose how long the message stays (max 30 days). 3. Optionally set a message icon. 4. Choose **Action when inbox clicked**. 5. Click **Apply**. ![Save to inbox settings: toggle enabled, Remove from inbox set to 7 days, Message icon URL, and Action when inbox clicked options](https://docs.pushwoosh.com/journey-elements-push-21.webp) Full field details and an example: [Save a push to Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/). ## Configure message delivery settings ### Set time to live for push delivery Use **Time to live** in **Delivery settings** to control how long Pushwoosh retains a push notification for delivery if the recipient is offline at the time of the send attempt. In **Time to live**, choose a preset period for how long Pushwoosh keeps this message available for delivery, from 1 hour to 3 weeks. ![Delivery settings in the Push journey element with the Time to live field and opened duration options.](https://docs.pushwoosh.com/journey-elements-push-19.webp) You can also choose **Custom range** and enter a specific number of hours, days, or weeks. The maximum period is 4 weeks. ![Delivery settings in the Push journey element with the Time to live field and opened duration options.](https://docs.pushwoosh.com/journey-elements-push-18.webp) Note The countdown starts from the moment Pushwoosh attempts to deliver the push in the journey, not from when the journey was triggered or the message was created. ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive push messages, preventing over-messaging and reducing churn. In the Push element settings, choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 messages in 9 days, additional messages exceeding this limit will be skipped. * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other push messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings**\ Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate**\ Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate**\ Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) ## Enable Best time to send If you want each user to receive a push notification when they are most likely to interact with pushes, enable the **Best time to send** option. The time for sending the message to each user will be calculated based on their behavior and previously sent messages’ performance. ![Best time to send option in Push element with default time and timezone settings](https://docs.pushwoosh.com/journey-elements-README-10.webp) Note To use this option, first, enable the [**PW_ApplicationOpen**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_applicationopen) and [**PW_NotificationOpen**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_notificationopen) default Events in your project. We recommend doing this a few days before sending the push to collect enough data. The accuracy of calculating the best time for each user depends on how many pushes you sent to this user earlier. If there is not enough data on the user, they will receive a push at the **Default time** you specify according to their timezone. Caution * The **Best time to send** is calculated for the next 24 hours after the user enters the journey point where this option is enabled. * If you’re using a [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) for the next step and the best time to send is later than the delay time, the user will wait until the best time, receive the message, and then proceed to the next step in the journey. * However, if the Time Delay is set to an exact date and time and the best time to send is later than that, the user will not receive the message and will exit the journey. ## Send to all user devices (from UserID) When enabled, the message will be sent to all the devices associated with an ID of a user who reaches this journey element. Thus, if a user has several devices, all associated with a single userID, that user will receive several messages, one per device. ![Send to all user devices (from UserID) toggle in the Push element settings, turned on](https://docs.pushwoosh.com/journey-elements-README-13.webp) ## Send to last active device Turn on **Send to last active device**. The message will be sent only to the device with the most recent app open under the user’s ID. ![Send to last active device toggle enabled in Push element with UI description below the label](https://docs.pushwoosh.com/journey-elements-push-20.webp) This option is mutually exclusive with **Send to all user devices (from UserID)**. When you turn one on, the other turns off. Example: Send to all user devices vs. Send to last active device A user has a phone and a tablet linked to the same User ID. The user last opened the app on the tablet. * **Send to all user devices (from UserID):** the push goes to the phone and the tablet. The user gets two notifications. * **Send to last active device:** the push goes to the tablet only. The phone does not receive it. The same toggle is also available when sending a [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#send-to-last-active-device). ## Split flow after a push Use **Flow options** in the Push element to branch the journey when users open the push, when the push is delivered to the device, or when you use both checks. ### Split flow by opens or ignores Split the remaining journey flow based on whether this push is opened or ignored. For example, email users who don’t open pushes or send another push to those who did. ![Split flow based on push opens in the journey](https://docs.pushwoosh.com/journey-elements-README-11.webp) To set it up: 1. Turn on **Split flow if message is opened**. The canvas adds the **Opened** and **Not opened** branches. 2. Set **How long to wait to check message opens** using **days**, **hours**, and **mins**. The maximum wait time is 7 days. 3. On the canvas, connect **Opened** and **Not opened** to the next elements in your journey. ![Set waiting period for opened and not opened branches](https://docs.pushwoosh.com/journey-elements-README-12.webp) If the wait time ends while the journey is paused, the user waits at this step. Once you resume the journey, the user continues along the **Not opened** branch. A user waiting on this split is lost if you pause the journey for longer than 7 days (this happens silently, with no log entry or metric), if you select **Stop campaign** while the journey is paused, or if you delete this Push element during editing. See [What happens to users already in the journey when you pause it](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#what-happens-to-users-already-in-the-journey-when-you-pause-it) for details. ### Split flow by message delivery This flow option depends on whether Pushwoosh can confirm that this push was delivered to the user’s device. Send users on the **Not delivered** branch to a fallback channel such as SMS, email, or in-app messaging so they still get your message. How Send to all user devices affects the Delivered branch With **Send to all user devices (from UserID)** turned on, Pushwoosh sends this push to each device tied to the user. For the delivery split, the user follows the **Delivered** branch as soon as delivery is confirmed on any of those devices. To set it up: 1. Turn on **Split flow if message is delivered**. The canvas adds the **Delivered** and **Not delivered** branches. 2. Set **How long to wait to check message delivery** using days, hours, and mins. Use the same pattern as for the open split. The maximum wait time is 7 days. ![Push element Flow options with split by opens, split by delivery, wait timers, and note when both splits are enabled.](https://docs.pushwoosh.com/journey-elements-push-15.webp) 3. On the canvas, connect **Delivered** and **Not delivered** to the next elements in your journey (for example a fallback message on **Not delivered**). ![Customer Journey canvas showing Delivered and Not delivered branches with user counts and percentages.](https://docs.pushwoosh.com/journey-elements-push-16.webp) Delivery split results appear in journey analytics on the canvas, including user counts and the percentage on each branch. ### When both delivery and open splits are enabled If both splits are enabled, devices with delivered pushes proceed to either the **Opened** or **Not opened** branch. Devices whose pushes are not delivered proceed to the **Not delivered** branch. ![Customer Journey canvas showing push delivery split branches with user counts and percentages on each branch.](https://docs.pushwoosh.com/journey-elements-push-17.webp) When you finish configuring this Push element, click **Save**. --- # SMS **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/ Send personalized SMS in journeys. Use presets and vouchers, track delivery, set frequency caps and send rate to boost engagement --- Tip To start working with the SMS channel, connect your SMS provider in **Settings → Configure Platforms → SMS**. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) Otherwise, sending SMS will not be available. **SMS** element indicates a point to communicate with a customer via SMS. Developer assistance needed To send SMS using Customer Journey Builder, first ask your developer team to associate customer phone numbers with **UserId** using the [/registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) method. The phone number must be specified in the “hwid” parameter. To send SMS to users within the journey, add the **SMS** element to the canvas next to any element. Choose an SMS preset, or click **Create content** to build a new one. ![SMS element Preset field with Cart reminder selected and an arrow on the Newest first sort button](https://docs.pushwoosh.com/shared-30.webp) To change the order of presets in the dropdown, click the sort button above it. The dropdown is sorted by **Newest first** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every SMS preset dropdown, in one-time messages and journeys alike. Note If you want to use an [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets), create it in the **Content** → **SMS presets** section. You can also use [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) in the message text. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets) ## Set message type Choose **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: Frequency capping and Silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: Frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![SMS element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/messaging-channels-sms-4.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Overwrite personalization When you select a preset, you can overwrite how the message is personalized: * **Personalise message with event attributes:** Enable this option to use event attributes or API-based entry placeholders instead of device tags as value sources in message content and presets. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Overwrite Liquid placeholders:** Enable this option to use values from the selected Event in Liquid personalization. Liquid tags that match event attribute names will use values from the event. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Use Vouchers:** Enable this option to use vouchers in message content. [Learn more](https://docs.pushwoosh.com/product/content/vouchers/) ![SMS element overwrite personalization options showing event attributes, Liquid placeholders, and vouchers toggles](https://docs.pushwoosh.com/messaging-channels-sms-5.webp) ## Configure message delivery settings ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to control how often users receive SMS messages, helping prevent message fatigue and opt-outs. You can choose one of the following options: * **Use global frequency capping settings** Apply the project-wide SMS limits set in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). *Example:* If the global limit is 3 SMS messages in 9 days, any additional messages will be skipped. * **Send messages without global frequency capping** Send the SMS regardless of how many messages the user has already received. Use with caution to avoid over-messaging and compliance issues. * **Use custom frequency capping** Set a custom SMS limit specifically for this message step. If the user has reached the limit, they will skip this message and move to the next step in the journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#customize-capping-for-specific-messages) Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings**\ Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate**\ Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate**\ Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) ## Flow options Enable the **Split flow depending on whether this message is delivered or not** toggle to split the journey based on whether the SMS is delivered. Then, set the delivery waiting time. If the SMS is delivered within the specified time period, the `"delivered"` branch will be activated. Maximum 7 days. ![Split flow depending on whether the SMS is delivered](https://docs.pushwoosh.com/shared-57.webp) ### Branch on RCS reply buttons When the selected SMS preset has an [RCS](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) template with quick reply buttons, the element branches on the button the user taps instead of on delivery. It gets a branch for each button, labeled with the button text, plus a **Not match** branch, and the canvas shows **Wait for reply: 24 hours** under it. ![SMS element Confirm delivery slot with Yes, No, and Not match branches and Wait for reply: 24 hours](https://docs.pushwoosh.com/shared-65.webp) * The user waits up to 24 hours for a tap. A tap sends the user down that button’s branch. If there’s no tap within 24 hours, or the phone doesn’t support RCS, the user goes to **Not match**. * The **Split flow depending on whether this message is delivered or not** toggle isn’t available, because the buttons branch the element. * A button whose text is an opt-out keyword (**STOP**, **STOPALL**, **UNSUBSCRIBE**, **CANCEL**, **END**, or **QUIT**) unsubscribes the user. Its branch stays on the canvas, but a tap on it never sends the user down that branch. * Only quick reply buttons get branches. A button that opens a link doesn’t. Caution If two RCS elements wait for a reply from the same phone number at the same time, a tap goes to the element that sent the most recent message. The other element moves the user to **Not match** after 24 hours. The branches come from the copy of the buttons Pushwoosh saves with the preset. If you change the buttons in Twilio, open the preset and save it again to update the branches. ### Branch when the provider rejects the message To send users whose SMS or RCS message the provider rejected to a separate branch, for example to try another channel: 1. In the element settings, turn on **Branch when the provider rejects the message**. 2. Save the element. 3. On the canvas, connect the **Rejected by provider** branch to the next element. A user goes to **Rejected by provider** when the provider refuses the message at sending, or when a later delivery report from Twilio or your SMS provider marks it as failed or undelivered. Errors that Pushwoosh retries don’t lead to this branch. * **With quick reply buttons:** a rejected message goes to **Rejected by provider** at once. Other users wait for a tap. * **With the delivery split:** the element waits for the time set there. * **Otherwise:** the element holds the user for up to 1 hour, and the canvas shows **Wait for refusal: 1 hour**. If the provider doesn’t reject the message within that time, the user continues through **Sent**. Without a connected **Rejected by provider** arrow, a user whose message was rejected follows the element’s other branches. Turning the setting off removes the branch and its arrow. --- # Telegram **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/telegram/ Send text-only Telegram messages in journeys with personalization, vouchers, and delivery-based reachability handling. --- The **Telegram** element sends plain-text Telegram messages to users who started your bot and entered a customer journey. ## Configure the Telegram element 1. Add the **Telegram** element to the journey canvas. 2. In **Message text**, enter the message body. 3. Keep the message text-only and within **4096** characters. ## Personalize message content You can personalize Telegram messages with tags and Liquid placeholders. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/) ![Telegram element settings with Message text field, voucher options, and Apply button.](https://docs.pushwoosh.com/journey-elements-telegram-1.webp) ## Use vouchers To include voucher codes: 1. Turn on **Use vouchers**. 2. Select **Voucher pool**. 3. Optional. Set **Assign tag**. 4. Add `{{voucher}}` to **Message text** where the code should appear. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) Once you configure the element, click **Apply**. ## Reachability and statistics If sending fails because of a temporary network issue, Pushwoosh retries automatically. If Telegram reports that the user blocked or removed the bot, that the chat wasn’t found, or that the account was deactivated, Pushwoosh unsubscribes the user from Telegram. These sends appear as **Chat Unavailable** (410007) on the **Errors** tab of the [message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#errors-tab). Nothing needs fixing: the person can’t receive messages from the bot anymore. See [Telegram errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/#telegram-errors). For an end-to-end setup guide, see [Send Telegram messages in Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/). --- # Viber **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/ Send Viber messages in journeys from a Rakuten template or freeform text with an image, button, or carousel, plus delivery rules. --- Use the **Viber** element to send Viber messages in a Customer Journey. Drag **Viber** onto the canvas, then choose the content and delivery settings below. Note Before you use Viber in a journey, [connect the channel](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/viber-configuration/) in **Settings → Configure platforms → Viber** and [register phone numbers as Viber subscribers](https://docs.pushwoosh.com/product/messaging-channels/viber/register-viber-contacts/). ## Select message content Choose how to define the message content. ### Template Select a Viber template from your Omni Messaging account. You create templates in Omni Messaging, not in Pushwoosh, and **Rakuten Viber** approves transactional templates. Messages sent with an approved transactional template are billed at the lower Transactional rate. In the template list, pick: * **Transactional template:** keeps an approved template attached. Only approved transactional templates appear in the list. * **Promo template:** a saved campaign template that doesn’t need approval. It loads its content as editable Freeform content, either text with an image and a button (set up under [Add an image and a button](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#add-an-image-and-a-button)) or text with carousel cards (set up under [Send a carousel](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#send-a-carousel)). The list shows only promo templates with text, an image, a button, or carousel cards. Promo templates with other content, for example a video, don’t appear. If you don’t see a template, check its content in Omni Messaging. To change the order of templates in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every Viber template dropdown, in one-time messages and journeys alike. ![Viber element with Template selected showing approved template dropdown in Customer Journey settings.](https://docs.pushwoosh.com/journey-elements-viber-1.webp) ### Freeform Enter plain-text content directly into the message field. Freeform messages don’t require template approval, but are billed at the Promotional rate. For transactional and OTP messages, [Rakuten Viber](https://www.forbusiness.viber.com/en/blog/post/transactional-message-templates/) requires an approved template. Freeform content sent for these messages is billed at the Promotional rate. ![Viber element with empty Template field and Message text filled for Freeform content.](https://docs.pushwoosh.com/journey-elements-viber-2.webp) Note **Template** and **Freeform** define how you write the message and which Viber rate applies. [**Message type**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#set-message-type) (**Marketing message** or **Transactional message**) controls delivery rules such as frequency capping. These choices are independent: rate is not the same as message type. You can use any content option with either message type. ### Add an image and a button On Freeform content (typed from scratch or loaded from a promo template), add an image and a button if you need them: * **Image:** enter a public image URL (`http://` or `https://`), or click the attachment icon and choose a JPG or PNG from your [media library](https://docs.pushwoosh.com/product/content/media-store/) (up to 10 MB). For a typed URL, Pushwoosh checks that it’s a valid `http://` or `https://` URL with a host, but not the file format or size. Viber shows the image as a square, so a square image won’t be cropped. * **Button text:** enter up to 30 characters. Requires message text and a button link. A button can’t appear on its own. * **Button link:** enter an `http://` or `https://` URL, a `viber://` deep link, or an Omni token (a placeholder your Viber provider resolves at send time, for example `{lp-100}`). ![Freeform Viber fields with Image URL, Button text, and Button link filled in the journey element](https://docs.pushwoosh.com/journey-elements-viber-5.webp) Note Switching to a transactional template clears the image and button. They don’t carry over when content is set to **Template**. ### Send a carousel A carousel is a row of 2 to 5 cards that the recipient swipes through under the message text. Each card has a title, an image, and one or two buttons. Note You can’t build a carousel from scratch in the journey element. Start from a carousel promo template in Omni Messaging, the platform that holds your account’s Viber templates. 1. In **Template**, choose a carousel template from **Promo templates**. The list shows template names only, so you can tell a carousel template only once you choose it. Its message text and cards load into the element, and the **Carousel** section appears. Note A carousel replaces the single image and button. While a carousel template is loaded, the **Image** and button fields are hidden and aren’t saved. Choosing another template clears the cards. 2. Check the message text. A carousel requires it. 3. Edit each card. You can’t add or remove cards in the element, because their number comes from the template. To change the number, edit the template in Omni Messaging and choose it again. * **Card title:** 2 to 38 characters. * **Image:** enter a public image URL or choose a JPG or PNG from your [media library](https://docs.pushwoosh.com/product/content/media-store/) (up to 10 MB), as for [a single image](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#add-an-image-and-a-button). A typed URL isn’t checked for format or size. The preview crops each card image to a square, so a square image shows in full. * **Button text** and **Button link:** required on every card. Button text is up to 10 characters. The link takes the same values as [a single button](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#add-an-image-and-a-button). * **Second button text (optional)** and **Second button link (optional):** fill in both or leave both empty. Second button text is up to 12 characters. If a card value goes over a limit, the field shows an error, for example **Card title must be 2 to 38 characters**, and the element can’t be saved until you fix it. ![Card 1 fields of a Viber carousel with title, image URL, and two buttons filled in](https://docs.pushwoosh.com/journey-elements-viber-8.webp) 4. To check how the carousel looks, open **Preview content**. The cards appear in a row under the message text. Scroll sideways to see all of them. ![Preview content of a Viber carousel with message text and a row of product cards, the third cut off at the edge where the row scrolls](https://docs.pushwoosh.com/journey-elements-viber-9.webp) To send a carousel through the API instead, see the [`carousel` field of the Messaging API](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#carousel). ## Personalize message with event attributes When your **Template** includes placeholders (for example an order ID or a verification code), map each one so every recipient sees their own values. Click a placeholder to open the variable picker. Set **Variable type** to **Event property**, choose the event, then pick the **Attribute** that supplies the value. Optionally add **Fallback text** for when the value is missing. You can also map placeholders from attributes on an API-based journey entry. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/) ![Variable picker mapping a template placeholder to an event property, attribute, and fallback text](https://docs.pushwoosh.com/journey-elements-viber-6.webp) Caution Map every required template placeholder to an event attribute before saving. ## Set message type Choose **Marketing message** or **Transactional message**. Message type sets delivery rules. It does not change the Viber rate from **Template** or **Freeform**. * **Marketing message:** use for campaigns and promos. Frequency capping applies. Users in the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) don’t receive the message. * **Transactional message:** use for operational or service messages. Frequency capping does not apply. Users in the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) still receive the message. No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![Viber element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/journey-elements-viber-3.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive Viber messages, preventing over-messaging and reducing churn. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 Viber messages in 9 days, additional messages exceeding this limit will be skipped. * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other Viber messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ## Split flow by delivery status To split the journey by Viber delivery status: 1. Enable **Split flow depending on whether this message is delivered or not**. 2. Set how long to wait for delivery (maximum 7 days). If the message is delivered within the wait period, users follow the **delivered** branch. Otherwise, they follow the **not delivered** branch. ![Split flow depending on whether the Viber message is delivered](https://docs.pushwoosh.com/journey-elements-viber-4.webp) Tip Remember to connect the **delivered** and **not delivered** branches to the next journey steps on the canvas. ## Use vouchers To include a unique voucher code from a pool in the Viber message: 1. Ensure your Viber message content includes the placeholder `{{voucher}}`. 2. In the Viber message step, toggle **Use Vouchers** to ON. 3. Select the appropriate **Voucher Pool** that contains your voucher codes. Make sure the pool is created in advance. 4. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This helps with segmentation and reporting. ![Viber element with Vouchers on, Voucher Pool selected, and optional Assign Tag field](https://docs.pushwoosh.com/journey-elements-viber-7.webp) [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) After configuring the Viber element, click **Save** to apply your settings. --- # Webhook **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/ Send journey data to external services with webhooks, map response values into the journey, and use them in later steps. --- Developer assistance needed You’ll need your developer team’s help to configure the Webhook element. Share this guide with them to get started. Webhooks let you send journey data to external services such as analytics, CRM systems, and marketing tools. You can: * Notify external systems when a customer takes an action in the journey * Send customer data to analytics tools * Trigger third-party emails, SMS, or WhatsApp on specific journey events Note Check out some examples of how to implement webhooks for different use cases and services: [Webhook Integration Samples](https://docs.pushwoosh.com/developer/guides/customer-journey/webhook-samples/) ## How to set up the Webhook element ### Add the Webhook element Drag-and-drop the **Webhook** element to the canvas. Place **Webhook** anywhere you’d like, keeping in mind what journey info you’re going to send to a third-party service. ![Customer Journey canvas with a selected Amplitude webhook step after entry and wait elements](https://docs.pushwoosh.com/journey-elements-README-40.webp) ### Name the Webhook step and specify the request URL and type In the **STEP NAME** field, enter a name for the webhook. It might be handy to name webhooks according to the services they send data to or the use case. Next, in the **URL** field, specify the request URL to which the data should be sent. Next to the URL field, select the request type from the **REQUEST TYPE** dropdown: `GET` or `POST`. ![Webhook configuration interface showing URL field and REQUEST TYPE dropdown for selecting GET or POST method](https://docs.pushwoosh.com/journey-elements-webhook-1.webp) Note The **URL** must be publicly reachable. Pushwoosh rejects private, internal, loopback, and link-local addresses, including cloud metadata endpoints, whether they come from the URL itself or from a redirect it points to. See [Timeout, retries, and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests) for what happens when a request is blocked. ### Configure headers In the **HEADERS** section, set the content type. By default, the content type is **application/json**. If the service you’re sending the webhook to requires another content type, enter the appropriate one in the **Content-Type** header value. Examples of content types are: * `x-www-form-urlencoded` * `text/plain` * `text/xml` Add additional headers if needed by clicking **+ ADD HEADER**. You can remove any header by clicking the ‘x’ icon next to it. Add whatever authentication header your endpoint requires, for example: * `Authorization: Bearer ` * `X-Api-Key: ` * `Authorization: Basic ` Only a static secret in a header is supported. OAuth2 token-exchange flows, mTLS, and request signing on the Pushwoosh side aren’t supported. You can also restrict the endpoint to Pushwoosh’s IP addresses instead of, or in addition to, a header secret. See [Pushwoosh IP addresses](https://help.pushwoosh.com/hc/en-us/articles/360000391803). Note Need to pass a per-user value, such as a token or ID, instead of a fixed secret? Insert a [Dynamic Data macro](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) into the **URL** or the JSON body, for example as a query parameter or a body field your endpoint reads. For HTTP Basic authentication specifically, do the following: 1. Open a plain text editor and type your username and password with no spaces, separated by a colon. For example: `:` 2. Encode this string into Base64. 3. Copy the resulting Base64 string (for example, ``). 4. In the webhook settings, add an Authorization header with the value: `Basic `. Make sure there is a space after the word “Basic”. ![Authorization header example for Basic authentication in webhook settings showing Content-Type and Authorization headers](https://docs.pushwoosh.com/journey-elements-webhook-2.webp) #### Mark a header value as secret Click the eye icon next to a header’s value to mask it. Pushwoosh hides that value everywhere it would otherwise leave the service: in the UI, in API responses, and in the journey’s version history. ![Webhook headers list with a masked Authorization value and disabled eye icon next to an unmasked Content-Type value with an active eye icon](https://docs.pushwoosh.com/journey-elements-webhook-5.webp) * **Automatic masking.** Headers whose names look like credentials are masked automatically, even if you never click the eye icon. This includes `Authorization`, `Proxy-Authorization`, `Cookie`, `Set-Cookie`, and any name containing `token`, `secret`, `password`, `credential`, `auth`, or `api-key`/`api_key`/`apikey` (with a hyphen, an underscore, or no separator). * **Change a masked value.** Click into the field showing `••••••••` and type the new value. There’s no button to reveal the stored value. The eye icon stays locked while the mask is shown. To remove the secret flag from a header, type a new value first, then click the icon. * **Rename a masked header.** Renaming a header whose value is currently shown as the mask clears that value. Enter it again under the new name. Renaming a header that currently holds a value you just typed keeps that value. Note In the journey’s version history, a change to a secret header’s value shows the mask on both sides of the diff. You can tell that a secret header changed, but not what it changed from or to. ### Add the JSON request body In the **DATA** section, enter your JSON request body. Make sure the request body is in correct JSON format. Note If there is no value for the Dynamic Data placeholder when the request is sent, Pushwoosh sends an empty string in its place. Example: ```plaintext { "hwid": "{{device:hwid}}" } ``` ### Use dynamic data and macros The **DATA BUILDER** panel allows you to insert dynamic information (such as user, device, tag, or event data) directly into your JSON request body. With Dynamic Data, you can include values specific to the individual user progressing through the journey. For this: 1. Select a **category**. You can pull data from three categories: * **Device:** Use Device data when you need technical information tied to the user’s device. * **Tag:** Use Tag data when you want to send information stored in the user profile. * **Event:** Use Event data when the webhook should send values from the triggering event of the journey. Note Values from the triggering event stay attached to the traveler for the rest of the journey, including across a [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/). They’re only lost if the traveler leaves the journey, for example on a cancel event, an exit, or when the journey ends. If a traveler goes through the same **Webhook** step more than once, the new response overwrites the value it mapped last time. 2. Select a **parameter** (for example, HWID, favourite category, etc.). 3. Pushwoosh generates a macro that looks like this: ```plaintext {{tag:Language}} ``` 4. Copy the macro and paste it into your JSON body in the DATA section. When the webhook runs in a live journey, Pushwoosh automatically replaces the macro with the actual value for that user. ![Insert Dynamic Data placeholders into the webhook request body](https://docs.pushwoosh.com/journey-elements-webhook-3.webp) #### Type additional placeholders manually A placeholder is a macro you type by hand instead of generating it from a DATA BUILDER category. The **DATA BUILDER** panel only covers **Device**, **Tag**, and **Event** data. Type these placeholders directly into the **URL**, **HEADERS**, or **DATA** section instead. They don’t appear in the panel: | Placeholder | Value | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `{{application_code}}` | The [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) of the app the traveler belongs to. | | `{{traveler:id}}` | The ID Pushwoosh assigns to this traveler for this journey run. | | `{{journey:uuid}}` | This journey’s UUID. | | `{{journey:name}}` | This journey’s name. | | `{{point:uuid}}` | This **Webhook** step’s UUID. | | `{{point:name}}` | This **Webhook** step’s **STEP NAME**. | | `{{event:name}}` | The name of the event that triggered this traveler’s entry into the journey. | | `{{device:platform}}` | The device’s platform, for example `Android` or `iOS`. | | `{{device:push_subscribed}}` | Whether the traveler is subscribed to push notifications — `true` or `false`. | | `{{now}}` | The current date and time, ISO 8601, UTC. | | `{{now:unix_ms}}` | The current time as Unix milliseconds. | | `{{tags:all}}` | Every tag value for the traveler’s device, as one JSON object. Use it unquoted, for example `"user_properties": {{tags:all}}`. Quoting it turns the object into an escaped string instead. | Note A placeholder that’s misspelled, or isn’t on this list or in the DATA BUILDER, is sent to the endpoint exactly as typed, curly braces included. There’s no error banner for it. Check the request body in [**Calls log**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) if a value you expected is missing. #### Keep a placeholder’s type in the JSON body A placeholder inside quotes always becomes a JSON string, whatever type the value actually is. The same placeholder on its own, with no quotes around it, keeps the value’s own type instead: a number stays a number, `true`/`false` stays a boolean, and a list becomes a JSON array. An unquoted placeholder must be the field’s entire value — `"age": {{tag:Age}}` works, but `"note": prefix{{tag:Age}}suffix` doesn’t, because everything outside quotes is written out exactly as typed and the extra characters break the JSON. ```plaintext { "age": {{tag:Age}}, "age_as_text": "{{tag:Age}}" } ``` Here `age` sends the tag’s numeric value (`34`), while `age_as_text` sends the string `"34"`. Use whichever the field on the receiving end expects. If the tag has no value, an unquoted placeholder still resolves to an empty string, not a number or `false`. See the note under [Add the JSON request body](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#add-the-json-request-body). Caution A date placeholder renders differently depending on where you put it. Quoted, it keeps the `YYYY-MM-DD HH:MM:SS` layout existing integrations already parse. Unquoted, it’s ISO 8601 instead, for example `2026-09-15T08:24:00Z`. The two aren’t interchangeable. Pick the position that matches your endpoint’s expected format. ### Map webhook response data to variables Besides sending data out, the **Webhook** step can keep values from the reply your service sends back. You give each value a name (**Attribute**). Later steps can use that name the same way they use other webhook response values. For example, set a Tag with [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), or schedule a [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response) from a date the service returned. For a full journey example, see [Using webhook response data in your journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/). Example: the CRM returns a user ID. You store it as **Attribute** `crm_user_id`. Then **Update user profile** writes it to a Tag. Before you map anything, get one sample response from the service. Ask your developer, or open a successful call in [**Calls log**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) after a test and look at the response body. You need the field names from that reply to build **Path**. In the **RESPONSE MAPPING** section, click **+ ADD MAPPING** and fill in two fields for each value you want to capture: * **Path:** the location of the value inside the response JSON body, with dots between levels * **Attribute:** the name you will use later in the journey ![Response mapping section with Path and Attribute fields and Add mapping button in webhook settings](https://docs.pushwoosh.com/journey-elements-webhook-4.webp) For example, if your CRM responds with: ```plaintext { "data": { "user": { "id": "789xyz" } } } ``` 1. Set **Path** to `data.user.id`. 2. Set **Attribute** to `crm_user_id`. After a user passes this step, later elements can pick **Attribute** `crm_user_id` the same way they pick other webhook response values. **Condition split** cannot use them directly. Mapped webhook values have no type. Save the value as a Tag first, then branch on that Tag. See [Compare a webhook value in Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/#compare-a-webhook-value-in-condition-split). Caution If the response is larger than 64 KB, none of it is mapped. Keep the response your endpoint sends back reasonably small if you plan to map values from it. For a single field, **Path** and values work like this: Note * Write object names with dots: `data.user.id`. * For a list, use a position number to take one item. The first item is `0`, so `results.0.code` is the `code` field of the first item. To map every item in a list, see [Map every element of an array](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-every-element-of-an-array). * You cannot map only some items in a list. For a list it is either one position or every item. * Pushwoosh keeps the value as the service sent it (text, number, or true/false). A number `100` stays a number. Text `"100"` stays text and will not work where the journey expects a number. For a **Time Delay** date, the service must return a format that **Time Delay** accepts. A decimal like `19.99` can go into a Tag of the **Price** type. * If the reply is not valid JSON, or **Path** does not match any field, that attribute is not created for this user. The **Webhook** step still finishes as usual. There is no error banner. Check the response body in **Calls log** and fix **Path** if the value is missing downstream. #### Map every element of an array Sometimes a webhook response doesn’t have just one value. It has a list, like every product in an order, every item in a cart, or every result from a search. Response mapping normally captures one value per field, so without this you’d only get one mapped value from that list, and the rest would be lost. Put `*` in the **Path** field where the list is. Pushwoosh then grabs a value from every item in the list, not just one position. For example, if the list is called `items` and each item has `item_name`, set **Path** to `items.*.item_name`. In **RESPONSE MAPPING**, click **+ ADD MAPPING** and fill in the two fields as usual, with `*` marking the list: * **Path:** the location of the value inside the response, with `*` where the list is. Example: `items.*.item_name`. * **Attribute:** the name you’ll use later. What you write here decides how you get the results back: * Include `{n}` in the name, for example `item_{n}`, to get each item as its own value, numbered from 1: `item_1`, `item_2`, `item_3`, and so on. `{n}` can sit anywhere in the name, for example `item_{n}_sku`. * Leave `{n}` out, for example `item_names`, to join every item into one value, separated by commas: `Sofa, Lamp, Rug`. ![Response mapping row with Path set to items.*.item_name and Attribute set to item_{n}](https://docs.pushwoosh.com/journey-elements-webhook-6.webp) Path list positions start at 0 (`items.0.item_name` is the first item). Attribute names built with `{n}` start at 1 (`item_1` is that first item). These are two different numberings. If you only need one item from the list, use a number in **Path** instead of `*`, for example `items.0.item_name`. ##### Example If your CRM responds with: ```plaintext { "items": [ { "item_name": "Sofa" }, { "item_name": "Lamp" }, { "item_name": "Rug" } ] } ``` * Set **Path** to `items.*.item_name` and **Attribute** to `item_{n}` to get three separate values: `item_1` is Sofa, `item_2` is Lamp, `item_3` is Rug. * Set **Attribute** to `item_names` instead to get one value: `item_names` is `Sofa, Lamp, Rug`. Note If one item in the list is missing the field you’re looking for (for example, no `item_name`), that item is skipped. The numbering doesn’t leave a gap for it. Example: three rows where the middle one has no `item_name` become `item_1` and `item_2` only. Only the first 50 items in a list are captured. Anything past that is dropped. The traveler still continues in the journey. Only the unmapped values are missing. You can use the mapped values later in the journey like any other webhook response attribute: * [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response): save a value to a Tag * [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response): wait until a date from the response * [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/): personalize message content **Condition split** cannot use them directly. Mapped webhook values have no type. Save the value as a Tag first, then branch on that Tag. See [Compare a webhook value in Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/#compare-a-webhook-value-in-condition-split). [Using webhook response data in your journey ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/) ### Timeout, retries, and failed requests Pushwoosh waits up to 10 seconds for a response. The whole **Webhook** step, including sending the request and processing the response, is capped at 30 seconds. #### Retries On a `500`, `502`, `503`, or `504` response, or a network error such as a connection failure, Pushwoosh retries the request once before giving up. A request that times out is not retried — see [What happens when a request fails](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#what-happens-when-a-request-fails) below. Any other non-2xx response is not retried either. Note A retry resends the exact same request. Make your endpoint safe to receive the same call twice (idempotent) if a duplicate would be a problem, for example charging a payment or creating a record twice. #### Rate limits Pushwoosh limits how many webhook requests an account can send per second. The limit is sized well above real traffic peaks, so normal journeys aren’t affected. A burst that exceeds it waits briefly for room before failing. #### Endpoint cooldown If an endpoint fails several times in a row, Pushwoosh stops sending it requests for a while instead of retrying a broken endpoint on every traveler, starting at 30 seconds and doubling on further failures up to 5 minutes. A single successful request clears this and resumes normal delivery. #### What happens when a request fails The **Webhook** element has no separate branch for failed requests. Any of the following drops the traveler from the journey at this step: | Cause | What triggers it | | ------------------------ | ----------------------------------------------------------------------------------------------------- | | Blocked endpoint address | The URL is private, internal, loopback, or link-local, including cloud metadata endpoints | | Rate limit | The account’s per-second webhook request limit is exceeded and no room opens up during the brief wait | | Endpoint cooldown | The endpoint failed several times in a row and Pushwoosh is temporarily skipping it | | Timeout | No response within 10 seconds, or the step exceeds its 30-second cap | | Network error | The request couldn’t reach the endpoint at all | | Non-2xx response | The endpoint returned an error status that isn’t retried, or was retried once and failed again | See [Request error](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#request-error). If you can’t afford to lose travelers here, have your endpoint always return a 2xx response and put any failure state in the response body instead, for example as a value your [Response mapping](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) can pick up. This applies to every **Webhook** step, including ones created earlier. An endpoint address that now matches the blocked-address rule above will start failing the same way. Unlike a failed request, a response that arrives but doesn’t map cleanly, such as invalid JSON, an unresolved **Path**, or a body over 64 KB, does not drop the traveler. See the note under [Response mapping](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) above. ### Test the Webhook Click **Test webhook** to verify that your webhook configuration is correct and the request is sent successfully. If a header still shows the stored mask, Pushwoosh fills in the real, saved value for the test request. The value never appears in your browser. This substitution only works for a header already saved on this exact step. A step you haven’t saved yet, or one you just copied, has no saved value behind the mask, so Pushwoosh sends the test request without that header. After a successful test (or a live call), open **Calls log**, expand the row, and compare the response body with each **Path**. The field must exist exactly as in **Path**. If the request succeeds but a later step has no value, **Path** usually does not match the response. The **Webhook** step will not show an error for that. ### Save your configuration Click **Save** to save your webhook configuration. ### Calls log Open the **Calls log** tab in the point’s drawer to see what Pushwoosh actually sent for this step: time, user, result, and duration, going back 30 days. Note **Calls log** only records requests Pushwoosh actually sent. A traveler dropped before that point, for example by the [rate limit](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#rate-limits) or an [endpoint cooldown](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#endpoint-cooldown), doesn’t get a row here. Filter by result (**Success**, **HTTP error**, **No response**) or search by the exact User ID or HWID. Click a row to expand it and see the request (method, URL, and body) and, depending on the outcome, either the response (status and body) or the error text. **Duration** covers the whole step, including time spent on an automatic [retry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#retries). Caution Request and response bodies over 4 KB are cut, marked with `…[truncated]`. Any value Pushwoosh sent in a header, such as a bearer token or API key, is replaced with `[redacted]` everywhere it would otherwise appear: in the URL, the bodies, and the error text, not just in a headers field. --- # WhatsApp **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/ Send personalized WhatsApp messages in customer journeys using approved Meta templates, custom content, dynamic content, and vouchers. --- The WhatsApp element in Pushwoosh Customer Journey allows you to send personalized WhatsApp messages to users. The WhatsApp element can be configured to use pre-approved templates from your Meta account or custom content created directly within Pushwoosh. > **Note:** You can initiate conversations only by using an approved Message Template. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/) ## Use custom content If a user initiates a conversation, you are allowed to respond with custom content within a 24-hour period. Custom content supports plain text (including placeholders, if any) and an optional media attachment. It does not allow other settings, such as platform-specific adjustments or languages. ![WhatsApp element configured to send custom content](https://docs.pushwoosh.com/journey-elements-README-45.webp) To attach media, enter a public HTTPS link to the file in the **Media** field, or click the attachment icon next to it to choose an existing file from your media storage. Pushwoosh detects the media type from the file’s extension: * **Image:** JPEG or PNG, up to 5 MB. * **Video:** MP4 or 3GPP (H.264 video, AAC audio), up to 16 MB. * **Document:** PDF, Word, Excel, PowerPoint, or TXT, up to 100 MB. WhatsApp downloads the file from the link itself, so the link must be public and the file must meet these requirements. Otherwise, WhatsApp won’t send the message. When media is attached, the message text is sent as its caption and can’t exceed 1024 characters. ![WhatsApp custom content Media field filled with an image link](https://docs.pushwoosh.com/journey-elements-README-46.webp) ## Use a pre-approved template from your Meta account Select this option to use a pre-existing template from your Meta account. After selecting a template, Pushwoosh displays a preview of the message content. If the template contains placeholders, each one represents a dynamic variable that must be configured before the message can be sent. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#configure-dynamic-variables) ![Preview of WhatsApp template selection with placeholders](https://docs.pushwoosh.com/whatsapp-messages-send-whatsapp-messages-3.webp) To change the order of templates in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every WhatsApp template dropdown, in one-time messages and journeys alike. If you want to use catalog message templates that allow users to browse your product catalog directly within WhatsApp, you can select from two types: **Basic catalog message** or **Multi-product catalog message** (allows you to select specific products to display). [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp) Tip If your WhatsApp template includes interactive message types, such as quick replies or call-to-action buttons, configure the journey to split the flow based on user interactions with these buttons. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#configure-quick-replies) #### Configure action URLs WhatsApp message templates can include action buttons (e.g., “Buy Now”) that open a URL when tapped. These buttons can be personalized using dynamic variables inserted as placeholders in the URL. The action URL is defined during template creation in your Meta Business account. If the URL includes placeholders (e.g., `{product_id}`), you must assign a value to each placeholder in Pushwoosh before sending the message. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#configure-action-urls) ![Preview of WhatsApp message template with dynamic action URL placeholders in Pushwoosh](https://docs.pushwoosh.com/whatsapp-messages-send-whatsapp-messages-4.webp) #### Add media attachments When using a template that includes a media file (image, video, or PDF), please note that the media is not stored in the template itself. These files are uploaded only as examples during template submission to Meta for validation purposes. To successfully send the message in a live campaign, you must specify the actual file URL in Pushwoosh. To add media, enter a valid, publicly accessible URL pointing to the actual media file in the **Image** field. Example: [`https://yourdomain.com/files/offer-banner.jpg`](https://yourdomain.com/files/offer-banner.jpg) **Note:** The message will not be sent if the media file URL is missing or inaccessible. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#add-media-attachments) ## Use a rich message A [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) built in **Content > Messenger presets** appears in the template dropdown with a **Rich message** badge. Select it to send its text, media, buttons, and variables for each language. To change them, edit the rich message. Until Meta approves the rich message’s WhatsApp template, the element delivers it only to users who wrote to you in the last 24 hours. [Get the template approved](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#get-the-whatsapp-template-approved) to reach everyone. When the rich message has quick reply buttons, the element gets a branch for each button, labeled with the button text, plus a **Not match** branch. If you switch to a template or rich message with different buttons and save the element, its arrows are removed. Reconnect the branches on the canvas. ![Content dropdown of the WhatsApp journey element with a rich message marked with the Rich message badge](https://docs.pushwoosh.com/content-rich-messages-14.webp) ## Branch when the provider rejects the message To send users whose WhatsApp message the provider rejected to a separate branch, for example to try another channel: 1. In the element settings, turn on **Branch when the provider rejects the message**. 2. Save the element. 3. On the canvas, connect the **Rejected by provider** branch to the next element. ![Journey canvas with a WhatsApp element, the Rejected by provider branch, and the Wait for refusal: 1 hour label](https://docs.pushwoosh.com/content-rich-messages-13.webp) A user goes to **Rejected by provider** when WhatsApp refuses the message at sending, for example to a user who hasn’t written to you in the last 24 hours, or when a later delivery report from Meta marks it as failed or undelivered. Errors that Pushwoosh retries don’t lead to this branch. * **With quick reply buttons:** a rejected message goes to **Rejected by provider** at once. Other users wait for a tap. * **With a read status split:** if [Split flow based on message read status](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#split-the-flow-based-on-message-read-status-optional) is on, the element waits for the time set in it. * **Otherwise:** the element holds the user for up to 1 hour, and the canvas shows **Wait for refusal: 1 hour**. If the provider doesn’t reject the message within that time, the user continues through **Sent**. Without a connected **Rejected by provider** arrow, a user whose message was rejected follows the element’s other branches. Turning the setting off removes the branch and its arrow. ## Set message type Choose **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: Frequency capping and Silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: Frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![WhatsApp element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/whatsapp-messages-send-whatsapp-messages-11.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive WhatsApp messages, preventing over-messaging and reducing churn. Choose one of the following options: Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 WhatsApp messages in 9 days, additional messages exceeding this limit will be skipped. * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other WhatsApp messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). ## Use vouchers You can personalize WhatsApp messages by attaching a voucher code from a predefined pool. This is useful for promotions, discounts, and loyalty incentives.\ For this, create a WhatsApp template that includes the placeholder `{{voucher}}`. To include a voucher in your WhatsApp message: 1. Toggle **Vouchers** to ON in the WhatsApp message step. 2. In the **Voucher Pool** field, select the pool containing your available voucher codes. Make sure to add the pool of vouchers in advance. 3. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This tag can be used for segmentation or reporting. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) After configuring the WhatsApp element, click **Save** to apply your settings. --- # Dynamic Content and Liquid Templates in journeys **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/ How to personalize customer journeys using Dynamic Content and Liquid Templates --- ## Overview Note Personalization with [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content) and [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) is available for both push and email messages. For greater relevance of your messages to users in the journey, you can add personalized content based on users’ behavior. For example, when sending an Abandoned Cart push, add the product name for better conversion – remind users of what exactly they wanted to purchase to make your message more convincing. When a user triggers some Event within the journey, i.e. performs some action in the app (e.g., adds a product to their cart or reaches a specific game level), use this Event’s attributes in your follow-up message’s content instead of inserting Tag values. Note Please take into account that personalization from Event attributes is available for presets only. Custom content can be personalized with Tag values instead. ## Dynamic Content personalization To personalize the message using [**Dynamic Content**](https://docs.pushwoosh.com/product/personalization/dynamic-content/), toggle **Personalize message with event attributes** while setting the Push or Email element on your journey canvas. Choose the personalization placeholder (or several placeholders) to populate with the Event attributes. ## Liquid Templates personalization To personalize the message using [**Liquid Templates**](https://docs.pushwoosh.com/product/personalization/liquid-templates/), enable the **Overwrite Liquid placeholders** option and select the desired Event. If the attribute name matches the tag name in the message, the attribute value will be added to the message text. Use Liquid placeholders if you need more flexibility when setting up message personalization. For example, you can add a list of purchased items and their price in the email text instead of a single value. ## Use cases ### Abandoned cart Anything can distract users from their journey through your app: sudden phone call, work email, or push from a messenger. Notice your users add products to their carts and do not complete the purchases? Send the personalized Abandoned Cart reminder to bring them back! For better conversion, add the product name to a push or email message – remind users what exactly they want to purchase to make your message more convincing. To build an Abandoned Cart campaign, 1. Start the journey with the Add product event triggered when a user adds something to their cart. 2. Then, place the Wait for event element next to the start event and choose Checkout success (or any other event triggered when a user completes their purchase). 3. Split the following journey into two branches based on whether the purchase is completed. To those who don’t checkout in a few minutes/hours, send a push or email message with their cart details. ### Appointment confirmation Building personal relationships with every client makes businesses successful, so you might want to provide your clients with all the help they need and all the care you can give. Send the appointment confirmation with all the details directly to the user’s inbox so that they can check it further not to miss their visit. 1. **Create an Event** with the attributes needed: specialist, date, and time 2. Prepare the push content and save it as a preset. On the journey [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#save-to-inbox) element, turn on **Save to inbox** if users should still find the message after they dismiss or miss the banner. 3. Start the journey with the Event you’ve created. 4. Set the push notification following the entry Event. Personalize the preset’s content with the *Appointment created* Event’s attributes. --- # Understanding entry elements **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/ Learn to use journey entry elements. Trigger real-time campaigns, schedule audience-based sends, and automate journeys with API calls --- Entry elements control when and how users enter a journey in Pushwoosh. Choosing the right entry type helps you reach users at the best possible moment, whether it’s in real time, at a scheduled time, or based on a specific audience. Each entry type comes with its own configuration options. ## Types of entry elements Pushwoosh offers three types of entry elements: #### Trigger-based entry Use this if you want to launch a journey after a user performs a specific action, like signing up, completing a purchase, or reaching a milestone. Best for real-time, behavior-driven messaging. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) #### Audience-based entry This method launches a journey for a predefined user segment. You can run it once, schedule it for specific dates, or set it to run periodically. You can also let users who start matching the segment after the journey is live enter automatically. Ideal for targeting users based on demographics, app usage, or imported data. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) #### API-based entry This entry type is triggered by an external API call, enabling journeys to start based on business events outside the app, such as price changes or inventory updates. Ideal for automation and integration with external systems. Developer assistance needed Requires developer support to configure and send API requests. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/) ## Combine entry elements You can add multiple entry elements to a journey. In this case, a user can enter the journey through any of the defined entry points. However, each user will enter the journey only once, even if they meet the conditions for multiple entry types. For example, if you use both Trigger-based and Audience-based entries, users who match the audience segment or perform the triggering event will be included. These conditions work independently and do not need to overlap, the journey will start for all users who meet either condition. ![Journey canvas combining multiple entry elements (Trigger-based and Audience-based)](https://docs.pushwoosh.com/journey-elements-README-7.webp) --- # API-based entry **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/ Launch journeys in real time with API-based triggers. Automate campaigns, target users with dynamic content, and control entry with segmentation --- Developer assistance needed You’ll need help from your development team to set up an API-based entry journey. Please share this guide with them. ## How it works API-based entry allows you to launch a customer journey the moment a specific business event occurs. To start a campaign, you have to send a special API request. Here are a few use cases for an API-based entry: * Inform customers when products are back in stock * Tell users a popular product’s price has decreased * Notify subscribers when a new podcast episode is out Unlike regular Events, all these business events may occur outside the app. For example, a product’s availability can only be checked in an external database. This is where an API-based entry comes in handy: you can set up sending a request to launch a journey whenever certain changes occur outside of the app (for example, in your external database). ![API-based entry element on the journey canvas](https://docs.pushwoosh.com/shared-33.webp) It works as follows: 1. Create a journey with an API-based entry. In the entry settings, you will find the template of the request that launches the journey. 2. Add segmentation conditions to the request using [Segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language). You can also add content placeholders to the request to change message content depending on the context. 3. Automate the request if needed. For example, information about a price change can be immediately sent from the database to the webhook. Once this happens, the webhook should automatically send the request to launch the journey. You can also send the request manually if you don’t need automation. You can send the request an unlimited number of times to change segmentation conditions or the message content. For more details, follow the instructions below. ## Set up a journey with API-based entry 1. Create a journey with an API-based entry: [](https://docs.pushwoosh.com/journey-elements-api-based-entry-1.webm "Create a new journey and select API-based entry") 2. Double-click on the API-based entry step. The entry configuration window will open. 3. You can modify the push and email content every time the journey is launched by using content placeholders. The value of each placeholder can be changed in the request. If you don’t need this option, you can skip this step. > For example, you are creating a journey to notify subscribers when a new podcast episode is released. Using a content placeholder, you can change the podcast title every time you launch the journey. First, add placeholder names in the API-based entry setup window. You can use any names that are convenient for you. ![Add content placeholder names in the API-based entry setup window](https://docs.pushwoosh.com/journey-elements-api-based-entry-2.webp) Now, create a [push preset](https://docs.pushwoosh.com/product/content/push-presets) or [email content](https://docs.pushwoosh.com/product/content/email-content/) and insert the placeholder instead of the text you want to modify. The placeholder must be in one of the following formats depending on your needs: * `{placeholder_name|format_modifier|}` – if the placeholder value is not specified when launching the campaign, users will see empty space in its place. * `{placeholder_name|format_modifier}` – if the placeholder value is not specified and has not been already assigned to a user (in case you used a Tag as a placeholder), the message will not be sent. Format modifiers * **CapitalizeFirst** – capitalizes the first letter in a placeholder value * **CapitalizeAllFirst** – capitalizes the first letters in all words in a placeholder value * **UPPERCASE** – switches all letters to uppercase * **lowercase** – switches all letters to lowercase * **regular** – inserts a placeholder value exactly as specified in the request ![Insert a placeholder into a push preset for dynamic content](https://docs.pushwoosh.com/journey-elements-api-based-entry-3.webp) Tip You can also use an [existing Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) name instead of a placeholder name. In this case, you have to configure overwriting this Tag value by the value specified in the request as described below. When configuring the Push or Email element in your journey, select the created preset and turn on the **Personalize message with event attributes** option. Select the placeholders you want to modify in the request when launching the journey. Choose the **API-based entry entry** as the source and the placeholder name as the dynamic attribute: [](https://docs.pushwoosh.com/journey-elements-api-based-entry-4.webm "Personalize message with event attributes from API-based entry") Click **Apply** to save changes. 4. In the entry configuration window, copy the request template to modify it: ![Copy request template from the API-based entry configuration window](https://docs.pushwoosh.com/journey-elements-api-based-entry-5.webp) Note To launch a journey via API, you must include a valid authorization token in the Authorization header. **Required header format** ```http Authorization: Api ``` **Example** ```http Authorization: Api c8dc6435-xxxxxxxxxxxxxxx ``` 5. Add audience filters to the `"filter"` parameter using [Segmentation language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language) or [copy the segmentation language](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#copy-segment-logic) from your segments. Set up the necessary [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) in advance. For example, to target users who added the *Socks* item to their *Wishlist*, the `"filter"` value must look as follows: `"filter": "A(\"12345-12345\") * "T(\"Wishlist\", EQ, \"Socks\")"` In this example, you must have a *Wishlist* Tag configured in your app. Note Your application code is automatically added to the `"filter"` parameter in the `A(\"12345-12345\")` format. Do not remove or modify it. Also, please keep in mind that quotes ("") and backslashes (\) should be escaped with a backslash (\) in JSON queries. Tip You can also target specific devices or users directly by passing an array of HWIDs in the `"hwids"` parameter or User IDs in the `"users"` parameter instead of using filters: ```json "users": ["user_id_1", "user_id_2", ...], "hwids": ["hwid_1", "hwid_2", ...] ``` 6. If you’ve set up placeholders, specify the desired content as their values: ![Specify placeholder values in the API request to launch the journey](https://docs.pushwoosh.com/journey-elements-api-based-entry-6.webp) 7. If you plan to restart your campaign frequently and don’t want the same users to enter the journey multiple times, set [Campaign entry limits](https://docs.pushwoosh.com/product/customer-journey/journey-settings#campaign-entry-limit). > For example, you’ve created a campaign to notify users about a price reduction for a specific product. You want to re-launch the journey a few times by sending several requests with different audience filters. In this case, you can add Campaign entry limits so that the notification is not sent repeatedly to users that match multiple filters. 8. If you want a journey to launch whenever a certain business event happens, automate the request using the webhook. Once the event occurs, the webhook should automatically send the request to start the journey. You can also send the request manually if you don’t need automation. Note * If you change the segmentation conditions when sending a new request, this will not affect users who have already entered the journey. * If you change the message content when sending a new request, all users will receive the new version of the message (including those who already entered the journey but have not received this message yet). --- # Audience-based entry **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/ Automatically start journeys for targeted user segments with audience-based entry. Set entry schedule and control when users enter, or add users when they match the segment later. --- **Audience-based entry** launches a journey for a pre-built segment of your app’s users. A segment includes subscribers who meet specific conditions. > For more information on Audience segments, refer to the [Segments guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/). To set up the Audience-based entry, drag the Audience-based entry element onto the canvas and select an Audience Source (i.e., a pre-built segment of app subscribers). ![Set up the Audience-based entry](https://docs.pushwoosh.com/journey-elements-entry-3.webp) You can manage segments directly from this element: * To edit the selected segment, click the **Edit** icon next to the **Audience Source** dropdown. * To create a new segment, click **Create Segment**. You can also upload a CSV file to create a segment and then start a journey for it. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import) Once the journey is activated, all users from the selected segment enter the journey. ## Entry schedule **Entry schedule** defines how users enter the journey. In the element configuration, choose between two entry types: * **One-time entry:** users enter the journey once – at launch or on a scheduled date. * **Recurring entry:** users enter the journey repeatedly based on the selected schedule. ![Entry schedule with One-time entry and Recurring entry options](https://docs.pushwoosh.com/journey-elements-audience-based-entry-1.webp) ### One-time entry When **One-time entry** is selected, choose when users enter the journey: * **Immediately:** users enter as soon as the journey is activated. * **Schedule for a specific day:** users enter on the date and time you set. #### Immediately Users enter the journey as soon as it is activated. No timezone sensitivity Users’ timezones are not considered. All users in the segment enter at once. To control when users enter and use timezones (e.g. subscriber’s device timezone), set **Schedule for a specific day** or use **Recurring entry** with time and timezone. #### Schedule for a specific day If you choose **Schedule for a specific day**, set the date, time, and timezone. ##### Timezone options * **Subscriber’s device timezone (default):** entry uses each subscriber’s device timezone. If timezone is missing, a fallback (e.g. GMT+01:00) is used. Click **Change fallback** to change it. * **Specific timezone:** pick a timezone from the dropdown for a fixed entry time. ![Audience-based entry: Schedule for a specific day with date, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-2.webp) ##### Choose what happens when a user’s scheduled time has already passed Choose what happens when a user’s scheduled entry time (in their local timezone) has already passed: * **No, exclude them from this journey:** the user will not enter the journey. * **Yes, let them enter immediately:** the user enters the journey right away. ![Audience-based entry: Include or exclude users whose local scheduled time has already passed](https://docs.pushwoosh.com/journey-elements-audience-based-entry-3.webp) #### Define who enters the journey Choose which users from the selected segment can enter: * **Only existing users (default):** only users who already match the segment at launch enter the journey. No one enters later. For example, send a one-time promo to users with the tag `Subscribed`. * **Existing and new users:** users who match the segment at launch enter first. After launch, users who become matches also enter. For example, run an onboarding journey for users with the tag `Registered`. * **Only new users:** no one enters at launch. Only users who become matches after the journey is activated enter. For example, run a post-purchase flow only for users who make their first order after activation. ![Audience-based entry: Who enters the journey with Existing and new users selected](https://docs.pushwoosh.com/journey-elements-audience-based-entry-8.webp) Segment limitations * For **Existing and new users** and **Only new users**, event-based segments are not supported. * Static segments are not supported for these options either. A segment created by [CSV import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import) is static, so new users are not added to it after launch. * In a segment written in [Segmentation Language](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/), the `Updated` and `BTTS` (best time to send) conditions are not supported for these options. * These limits also apply to every segment [combined into](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) the selected segment. * If a segment breaks these limits, saving or launching the journey fails with the error **Automatic entry isn’t supported for event-based and static segments. Please select a different segment or disable this option.** To fix it, click the **Edit** icon next to **Audience Source** and remove the condition or segment that breaks the limit, or switch to **Only existing users**. * The check runs only when you save or launch the journey. An already launched journey keeps working until you save changes to it. * With [RFM segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation), users enter only after you refresh their data. Go to **Audience > RFM Segmentation** and click **Recalculate** periodically to update RFM tags and allow new matching users to enter. #### When entry closes This setting is available only when both are true: * **Entry schedule** is set to **One-time entry**. * **Define who enters the journey** is set to **Existing and new users** or **Only new users**. With **Only existing users**, no one enters after launch, so there’s no window to close. Choose whether new users can keep entering: * **Never (default):** new matching users keep entering for as long as the journey runs. * **On a specific day:** after the date and time you set, no new users enter. Set the date, time, and timezone the same way as for [Schedule for a specific day](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#schedule-for-a-specific-day). Users already in the journey continue: waits, sends, and branches keep working as before. ![Audience-based entry: When entry closes set to On a specific day with date, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-9.webp) If you keep **Subscriber’s device timezone**, entry closes at the set time in each subscriber’s local timezone. So it closes in waves over up to 24 hours (UTC−12…UTC+12). Use the same timezone option for start and close Pick the same timezone option for entry start and entry close: subscriber’s device timezone for both, or a specific timezone for both. Otherwise, the entry window can be empty in some or all timezones, and the journey won’t launch: * **Empty in all timezones.** Rejected with **End entry must be later than start entry**. * **Empty only in some.** Rejected with **End entry is not later than start entry in timezones: …**, naming the affected ones. ##### If the close date has already passed A close date in the past behaves differently depending on when you hit it: * **When you set the date:** a past date shows **This date has already passed. Select a future date.** * **When you launch the journey for the first time:** the launch is rejected with **The end entry date has already passed**. * **When you resume a journey you paused after its close date:** this check doesn’t apply. The journey resumes, and entry simply stays closed. ##### Change the close date The close date field is locked while the journey is running. To change it: 1. [Pause the journey](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#editing-an-active-journey). 2. Set the new date. 3. Click **Resume campaign**. ##### Campaign status after entry closes Once entry closes, the campaign header dropdown that normally reads **Campaign is active** switches to: * **Active · entry closed** * **Active · entry closed · N in flight** while N users are still moving through the journey See [Campaign statuses](https://docs.pushwoosh.com/product/customer-journey/journeys-list/#campaign-statuses). ### Recurring entry When **Recurring entry** is selected, users enter the journey repeatedly. Recurring entry doesn’t offer a close-date option. That’s only available for [One-time entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#when-entry-closes). Choose the schedule: * **On specific dates:** users enter on the dates you pick. * **Periodically:** users enter at a fixed interval (e.g. every 1 day). Note The segment is recalculated before each scheduled entry, so users who joined the segment after the last entry also enter at the next scheduled time. Users already in the journey at the moment of the next start do not enter again. #### On specific dates Users enter the same journey on the dates you choose (e.g. the same reminder or signup flow runs on each scheduled webinar or training date). 1. Select **On specific dates** and pick the dates in the calendar. Click **+ Add Date** to add more dates. 2. Set the time. 3. Set the timezone the same way as for [Schedule for a specific day](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#schedule-for-a-specific-day). If a subscriber’s scheduled time has already passed, they are included on the next scheduled date. ![Audience-based entry: Recurring entry on specific dates](https://docs.pushwoosh.com/journey-elements-audience-based-entry-5.webp) #### Periodically Users enter the journey at a fixed interval on the days you choose (e.g. a fitness app runs the same daily 8 AM reminder journey every day). 1. Select **Periodically**. 2. In **ENTRY EVERY**, set the interval (e.g. every 1 day, every 2 weeks). 3. Pick the days (of the week or month, depending on the interval). 4. Set the time and the timezone the same way as for [Schedule for a specific day](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#schedule-for-a-specific-day). If a subscriber’s scheduled time has already passed, they are included on the next scheduled day. ![Audience-based entry: Recurring entry periodically with interval, selected days, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-6.webp) Note [RFM segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation) recalculate automatically on a 24-hour schedule and right before a journey checks an RFM-based entry condition, so a recurring journey doesn’t need a manual recalculation before each scheduled entry. If a refresh can’t complete in time, the entry check uses the last successfully calculated segment instead. --- # Trigger-based entry **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/ Trigger journeys automatically when users perform key actions. Set up event-based entries for timely, personalized engagement. --- A **Trigger-based entry** starts the journey when a user performs a specific event (for example, takes a specific action). Tip You can add multiple Trigger-based entry points. In that case, any of them will start the journey. ![Multiple Trigger-based entry points on a single journey canvas](https://docs.pushwoosh.com/journey-elements-README-6.webp) To set up a Trigger-based entry, locate the entry element on the canvas and select the Event that will serve as the trigger. > For more information on how to configure events, see [Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) documentation. If the selected Event has attributes, you can narrow the entry conditions using those attributes. Click **Add condition** when editing the element, then choose an attribute from the drop-down list and define its operator and value. > For the available operators, see [Tag operators by type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#tag-operators-by-type). ![Select the event that will serve as the trigger](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-1.webp) Important * Include **either the HWID (hardware ID) or the User ID** in your [`/postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) API request for the **Start Event**. If you send only a User ID, Pushwoosh resolves the user’s device automatically. * Entry requires that the user has at least one registered device. A User ID with no associated device cannot enter the journey. ## Define who enters the campaign Define which user(s) should enter the journey when a specific event occurs. Use the **Who enters the campaign?** dropdown to choose between the two available modes: ### Users who perform the event (default) The user who triggers the event is the one who enters the journey. ![Select users who perform the event](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-7.webp) **Example**\ A user completes a purchase (`CheckoutSuccess` event). That same user (e.g., `User ID: 123`) enters a post-purchase journey that includes a thank-you message, product recommendations, or a satisfaction survey. ### Users from an event attribute Instead of enrolling the user who triggered the event, you can pass one or more [`User IDs`](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) in a custom event attribute. The users listed in that attribute will enter the journey. For this, select **Users from an event attribute** and choose an attribute key (e.g., `recipient_ids`, `target_user_id`). This key should match your event payload structure. ![Select users from an event attribute](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-8.webp) Developer assistance required To use the **Users from an event attribute** mode correctly, your app or backend must send the correct payload structure. Please share the examples below with your development team. ##### Example payload (single user) ```json { "application": "XXXXX-XXXXX", "event": "invitation", "attributes": { "targetId": 321 }, "userId": 123 } ``` User `321` (from `targetId`) enters the journey. ##### Example payload (multiple users) If you provide multiple user IDs, use a JSON array of strings. ```javascript { "application": "XXXXX-XXXXX", "event": "invitation", "attributes": { "targetIds": [1,2,3,4] }, "userId": 123 } ``` Users `1`, `2`, `3`, and `4` will enter the journey. #### Use cases **Comment notifications** When someone comments on a user’s post, the post owner is notified. **Example:** A comment event includes the post owner’s ID in a `target_user_id` attribute. That user enters a journey to receive the notification. **Referral programs** When a referred user signs up or makes a purchase, the person who referred them is added to the journey. **Example:** User `123` triggers the event, and the referrer `456` (from the `referrer_id` attribute) enters a reward campaign. **Gift purchases** When a user purchases a gift, the recipient is added to a journey using the `recipient_user_id` from the event payload. **Example** User `123` buys a gift for user `456`, who then receives a notification, onboarding flow, or thank-you message. ## Set entry availability Control when users can enter a journey via a trigger-based entry. You have two options: * **Allow entry at any time**: Users can enter the journey whenever the trigger event occurs. * **Limit entry to a specific period**: Users can only enter the journey if the trigger event happens within the selected date range. When limiting entry, select the **start date**, **end date**, and the **timezone**. The entry window begins at **00:00** on the start date and ends at **23:59** on the end date, based on the chosen timezone. ![Set entry availability](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-2.webp) ## Control how many sessions a user can have at the same time Decide whether each user can join only one journey at a time, or take part in several journeys in parallel. Choose one of the following options from the dropdown: * One active session per user * Multiple active sessions per user #### One active session per user Each user can only have one active session in the campaign. They must complete or exit the current session before starting a new one. ![Select one active session per user](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-3.webp) ##### Use cases * **Onboarding flows** where a user should not restart until they finish the current journey * **Subscription renewal reminders** where users should not receive overlapping notifications * **Limited-time offers or trials** where only one active campaign run is allowed per user * **Feedback collection campaigns** to ensure each user provides input once before re-entering #### Multiple active sessions per user Users can have more than one active session in a campaign. Each session must be identified by a unique event attribute (e.g., `order_id`, `product_id`). To set this up: 1. Select **Multiple active sessions per user** in the dropdown. 2. Choose a session identifier attribute (e.g., `order_id`, `product_id`). This attribute will distinguish one session from another. ![Select Multiple active sessions per user](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-4.webp) Important This attribute must be included in all relevant journey events (for example, in **Wait for trigger** or **Conversion Goals**). If the session identifier attribute is missing, the system cannot link the event to a specific session. Instead, the event will be applied to all active sessions for that user. **Example** * `OrderPlaced` with `order_id = "1001"` → starts **Session 1** * `OrderPlaced` with `order_id = "1002"` → starts **Session 2** If an `OrderReady` event is sent without `order_id`, both Session 1 and Session 2 will be marked as “ready,” even if only one order actually is. ##### Use cases * **E-commerce purchases**: each order starts its own journey, so multiple orders from one user can be tracked independently. *(attribute: `order_id`)* * **Referral programs**: each referral creates a new session, allowing one user to refer multiple friends. *(attribute: `referral_id`)* * **Loyalty programs**: track points or rewards from different transactions running in parallel. *(attribute: `transaction_id`)* * **Ticket bookings**: each booking launches its own journey, so users can manage multiple tickets at once. *(attribute: `booking_id`)* * **Appointment scheduling**: every appointment is handled as a separate session. *(attribute: `appointment_id`)* ## Control whether users can re-enter the campaign Decide what happens when a user who is already in the journey triggers the entry event again. Choose one of the following options from the dropdown: * Do not allow re-entry (default) * Allow re-entry #### Do not allow re-entry Users who are already in the journey will not re-enter it. If a user is in an active session and triggers the entry event again, the new trigger is ignored and the user continues their current session. **Use cases** * **Welcome and onboarding series** where a new user goes through the journey once from start to finish and keeps their place if they trigger the event again instead of being sent back to the beginning * **One-time offers** where each customer should receive a promo or discount only once, even if they trigger the event multiple times * **Educational and nurture flows** where subscribers keep moving through the content without restarting and getting the same emails again #### Allow re-entry Users who are already in the journey will be removed from their current session and re-entered with a new session. Each time a user triggers the entry event, their current session ends and a new one begins. **Use cases** * **Price-drop alerts** where the restart must pick up the new price, not keep notifying about the outdated price from the previous session * **Win-back campaigns** where a returning inactive user should always start from the first message in the sequence Note Re-entry controls what happens to users who are already in the journey. It does not affect how many sessions a user can run in parallel. That is configured under [Control how many sessions a user can have at the same time](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-how-many-sessions-a-user-can-have-at-the-same-time). Once you’ve configured the entry element, click **Apply** to save the changes. ## Example scenario: E-commerce order journey with multiple sessions Imagine you want to send notifications for every order a user places. Each order should have its own journey session. To do this, you’ll use the `OrderPlaced` event as the trigger and the attribute `order_id` as the session identifier. 1. Create the event `OrderPlaced` and make sure it includes the attribute `order_id`. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) ![Create OrderPlaced event](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-5.webp) 2. Set this event as the trigger for your journey. ![Set this event as the trigger for your journey](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-6.webp) 3. In the session settings, select **Multiple active sessions per user** and choose `order_id` as the session identifier. ![select Multiple active sessions per user](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-9.webp) In this setup, each new order starts a separate journey session. * `OrderPlaced` with `order_id = 1001` starts **Session 1** * `OrderPlaced` with `order_id = 1002` starts **Session 2** 4. Next, add a [Wait for Trigger element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) to track when an order is ready for pickup or delivery. Use the event `OrderReady`, which must also include the same `order_id`.\ This ensures that each order is updated in the correct session: * `OrderReady` with `order_id = 1001` applies only to **Session 1** * `OrderReady` with `order_id = 1002` applies only to **Session 2** Important If the event does **not** include `order_id`, the system cannot tell which session it belongs to, and the event will apply to all active sessions for that user. ![Use the event OrderReady in the Wait for Trigger element](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-10.webp) 5. Finally, add a [conversion goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), for example the `OrderDelivered` event. This event must also include the same `order_id` so it can be matched to the correct session. * If `OrderDelivered` includes `order_id = "1001"`, it records a conversion for **Session 1**. * If `OrderDelivered` includes `order_id = "1002"`, it records a conversion for **Session 2**. Important If `order_id` is missing, the conversion will apply to all active sessions for that user instead of just the intended one. ![select Multiple active sessions per user](https://docs.pushwoosh.com/journey-elements-trigger-based-entry-11.webp) --- # Getting started with flow controls **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/ Use flow control elements to branch, delay, run A/B/n tests, and update user profiles as users move through a customer journey. --- Flow controls in Pushwoosh Customer Journey Builder allow you to define the logic and structure of your customer journeys. These elements help manage how users progress through paths based on segmentation, behavior, availability, timing, and more. Use them to personalize user experiences, control message delivery, and enhance engagement. ### Condition split The Condition Split element divides a journey into branches based on user segments or tag values. This allows you to direct users along different paths and deliver targeted content to each audience group within the same journey. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) ### Wait for Trigger The **Wait for Trigger** element pauses the journey until a user triggers a specified event or set of events. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) ### A/B/n split The **A/B/n split** element enables testing of different message sequences to determine which performs best. Use this element to optimize content delivery and improve conversion outcomes based on real-time performance data. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) ### Reachability check The **Reachability check** element routes users to the first channel they’re reachable on, from a list you order yourself, with a fallback branch for everyone else. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) ### Holdout Split The **Holdout Split** element holds back a share of users from part of a journey and reports their goal conversion against everyone who continued, with per-goal uplift and significance. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) ### Time delay The **Time delay** element holds users at a journey step for a specified duration. The delay can be fixed or based on specific times, dates, days of the week, or dynamic values from user tags or event attributes. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) ### Update user profile The **Update user profile** element modifies user Tag values either manually or dynamically based on event data. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) ### Loop The **Loop** element repeats a set of connected steps up to a limit, with an optional minimum interval between passes, instead of duplicating them on the canvas. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/loop/) ### Audience sync The **Audience sync** element sends users from a journey step to a selected Meta audience. You can add users for targeting or remove users for suppression. [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/audience-sync/) A journey branch ends on its own once it has no outgoing connection — no dedicated element is required. To mark the end of a branch explicitly, use the [**Exit**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element. --- # A/B/n Split **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/ Run A/B/n tests in journeys to compare message variations, optimize conversions, and tailor content. Select winners manually or automatically --- [Youtube Video: Learn how to set up and analyze A/B/n tests in Pushwoosh Customer Journey Builder](https://youtube.com/watch?v=hYiwYBOLu64) Use the **A/B/n Split** element to test which message sequences perform best for your [Conversion Goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals). Optimize communications by finding the most effective approach for your audience. ![A/B/n Split element on the journey canvas](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-1.webp) ## Configure A/B/n split settings After placing the **A/B/n Split** element on the canvas, double-click it to configure test settings. ### Set the number of branches Use the **Branches** dropdown to choose how many variations you want to test (up to 4). By default, Branch A is the control group. #### Define user distribution across branches Set the percentage of users to be routed to each branch, ensuring the total adds up to 100%. To evenly split users between branches, click **Reset Distribution**. ![Configure branches and define user distribution across branches](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-14.webp) ### Choose how the winner will be selected You have two options: ##### You select the winner manually You will review results and manually choose the winning branch later. This requires ongoing monitoring and manual deactivation. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#analyze-the-results) ![Manual winner selection setting in A/B/n Split test configuration](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-16.webp) ##### The winner is selected automatically by goal The system monitors the campaign goal and deactivates weaker branches once statistical significance is achieved. New users are then directed to the winning branch. When automatic winner selection is enabled, choose how the system should evaluate the winner: * **First goal to achieve significance**. The system monitors all assigned goals and selects the first to reach statistical significance. * **Select a specific goal** (for example, `CheckoutSuccess`). This goal will be used to evaluate which branch performs best. ![Automatic winner selection by goal in A/B/n Split settings](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-17.webp) Once configured, click **Save** to apply your test settings. ## How to set up and run A/B/n tests ### Set up a test #### Pause the journey If you want to set up A/B/n testing in an active journey, pause it first. To learn more about editing an active journey, [read this article](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#editing-an-active-journey). #### Define at least one Conversion Goal Set at least one Conversion Goal that will be used for calculating test results. For example, if you want to test which of two push notifications is the most effective, you can set the *PW_InAppPurchase* Event as the Conversion Goal. ![Set a Conversion Goal for the A/B/n test](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-2.webp) If you specify several Conversion Goals, the results are calculated for each goal separately. #### Add the A/B/n Split element Place the **A/B/n Split** element to the canvas after any other element and double-click it to configure the test. [](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-3.webm "Add the A/B/n Split element to the journey canvas") #### Set branches and user distribution Select the number of branches. You can add up to four branches to your test. The first branch is always considered a control group against which results in other branches are compared. Keep following this guide to learn how to set up this branch depending on your needs. ![Set the number of branches for the A/B/n test](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-4.webp) Next, set the percentage of users going to each branch. The total of all branches should be 100%. According to the percentage you set, users in the journey who reach this step will be split randomly into the specified number of branches. Please note that users assigned to Branch A will be considered a control group against which results in other branches will be compared. The next step describes how to set up Branch A for different cases. Note You may want to set a higher percentage for Branch A (the original version) to carefully test a new campaign version without risking lower conversions. However, we recommend setting up an **even distribution**. In this case, you will get statistically significant results faster. ![Set user distribution across branches](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-5.webp) #### Select winner evaluation mode Choose how the winner is determined: * **Automatic selection by goal.** The system tracks a defined goal, detects the best-performing branch using statistical significance, and routes new users to the winner automatically. * **Manual selection.** You will review the test results and manually decide which branch performs best. This option requires you to monitor the test and deactivate underperforming branches manually. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#analyze-the-results) #### Configure content for each branch Now you can set up communication flows for each branch. Since Branch A is a control group, you need to configure it in a certain way depending on your case: * **Test the existing communication flow against new versions.** In this case, the existing version should be in Branch A. Also, ensure the Conversion Goal can be completed in any branch, including Branch A. * **Test only new versions against each other.** Since there is no old communication flow, one of the new versions will be marked as a control group (the one presented in Branch A). Make sure the Conversion Goal can be completed in all branches. * **Test an existing version without any communication against new communication flows.** For example, you want to add one more push at the end of a journey to see if the campaign performance increases compared to the original version without this push. In this case, the ‘empty’ version should be presented in Branch A. To get reliable results, add an equal [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) to each branch: ![Configure branches with different communication flows for testing](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-6.webp) #### Launch the journey If everything is ready, launch the journey. ### Analyze the results As soon as the test is launched, you can see real-time statistics on it. On the journey canvas, see the number of users who pass through each branch: ![View user count for each branch on the journey canvas](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-7.webp) To view the test results, double-click on the **A/B/n Split** element. You will see the statistics for each branch, including the conversion to the selected goal and the metrics used to calculate statistical significance. ![A/B/n test statistics for each branch](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-8.webp) If you have set several Conversion Goals, you can view the results for each of them by selecting the required Goal from the list: ![Select a Conversion Goal to view specific test results](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-9.webp) Automatic mode If you enabled automatic winner selection, Pushwoosh will continuously monitor the selected goal. Once one branch reaches statistical significance, it will be marked as the winner. Weaker branches will be automatically deactivated, and new users will be routed exclusively to the winning branch. No manual action required. If your test has a statistically significant winner and you are using manual winner selection, you can instantly turn off less-performing branches by clicking **Keep only winner**. New users in the journey will be directed to the winning branch. Users who already went to deactivated branches will continue going through them until they complete the journey. ![Keep only winner option in manual winner selection](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-10.webp) Note You can also [deactivate branches manually](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#deactivating-branches), but this requires pausing the journey. You can view test results for a specific period of time. To do this, select the desired period on the left panel and double-click on the **A/B/n Split** element. You will see the statistics for the specified time. ![View test results for a specific time period](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-11.webp) The test results are calculated as follows: * The **conversion** is calculated based on the number of users in the branch and the Conversion Goals completed. Branch A will be marked as the Baseline, as it represents the control group. * A Conversion Goal counts only toward the branch the user actually went through. If branches later lead to the same element, for example a shared Time Delay or Exit, a goal reached after that element still counts toward the user’s own branch only. * When calculating **statistical significance**, *Z-Score* and *P-Value* are taken into account. If the result is valid, it will be marked as *Significant*. * A branch is considered the **winner** if it has the highest conversion, and this result is marked as *Significant*. Goals have been counted per branch since October 2026. Results for some earlier dates may have been recalculated the same way, so a test that ran before that can now show different numbers. For dates that weren’t recalculated, a goal reached after branches merge is counted in every branch that leads to it. Note If the result is marked as *Significant*, it means that its confidence level is 90%. If this percentage is not enough for you, you can calculate the result by specifying a different confidence level. For example, you can use this [calculator](https://abtestguide.com/calc/). ### Deactivate branches Automatic mode In Automatic mode, if a statistically significant winner is identified, Pushwoosh will automatically disable underperforming branches, route all new users to the winning branch, and allow existing users to continue in their assigned branch until they complete the journey. No manual action is required. If Pushwoosh has identified a statistically significant winner, you can instantly turn off less-performing branches by clicking **Keep only winner** in the test statistics window. In this case, you don’t need to pause the journey. If some branch performs poorly, you can deactivate it. New users will no longer get into the deactivated branch. To deactivate a branch, pause the journey first: ![Pause the journey to deactivate a branch](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-12.webp) After that, double-click the **A/B/n Split** element. Set 0% for this branch and redistribute the percentage of users going to the remaining branches: ![Deactivate a branch by setting its user distribution to 0%](https://docs.pushwoosh.com/journey-elements-running-a-b-n-tests-13.webp) --- # Audience sync **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/audience-sync/ Sync users from journeys to a Meta or Google Ads audience for ad targeting or suppression, with platform, account, and audience selection in one step. --- The **Audience sync** element updates a Meta Ads custom audience or a Google Ads audience when users reach this step in a journey. Choose **Add users to audience** to include them in ad targeting, or **Remove users from audience** to stop showing ads to them. ## Before you start Make sure the ad platform integration you plan to use is configured for your project. If neither integration is connected, the element shows **Configure your ad account** and cannot run sync. Learn how to set up integration: [Meta Ads integration](https://docs.pushwoosh.com/product/integrations/meta-ads-integration/) or [Google Ads integration](https://docs.pushwoosh.com/product/integrations/google-ads-integration/). Note For better match quality on Meta, configure user data mapping in Meta integration settings using **Map user data**. Google Ads has no separate mapping step — it matches users by hashed email, hashed phone, and MADID only. ## Configure audience sync 1. Double-click **Audience sync** on the canvas. 2. Enter **Step name**. 3. In **How to sync users info to the ad audience**, select one option: * **Add users to audience** * **Remove users from audience** 4. In **AD PLATFORM**, choose **Meta Ads** or **Google Ads**. 5. In the account field below it (**Meta Ads account** or **Google Ads account**), select one connected account for the platform you chose. 6. In **Audience**, select an existing audience on that platform. ![Audience sync panel with Audience dropdown and selected Meta Custom Audience](https://docs.pushwoosh.com/integrations-meta-ads-integration-10.webp) 7. Click **Apply**. Note A point created before this update keeps running against Meta Ads — **AD PLATFORM** on it defaults to **Meta Ads**, and its behavior does not change. How each user is processed * Users who match required identifiers are synced with the selected audience. * Users without matching identifiers are skipped and continue the journey. * If the selected ad account is disconnected, users cannot be synced at this step. * On Google Ads, sync runs in the background: the step marks a user as sent and moves on, and Google applies the add or remove shortly after. A Google audience with fewer than 1,000 matched users is not used for ad targeting, even though the sync itself reports as successful. --- # Exit from journey **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/ Use the Exit element to end a journey branch. Mark the end of a communication flow and ensure users complete the journey as designed --- **Exit** is a point that marks the end of a journey branch. A branch also ends on its own when it has no outgoing connection, so adding **Exit** is optional — use it when you want to mark the end of a branch explicitly. Note Journeys that already have an Exit element placed keep working and displaying exactly as before. ![Exit element marking the end of a journey branch on the canvas](https://docs.pushwoosh.com/journey-elements-README-43.webp) --- # Holdout Split **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/ Hold a share of users back at one journey point, then compare Conversion Goal rates to users who continued, with uplift. --- Use the **Holdout Split** element to hold a share of users back from the rest of a journey, then compare their goal completion against everyone who continued past that point. Unlike the account-wide [Control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/), which is fixed per application and blocks marketing messages everywhere, a Holdout Split applies only inside one journey, at one point in its flow. ## When to use Holdout Split * **Measure one journey’s impact at a chosen point.** Hold out a share of the users reaching this point and see whether the steps after it move your Conversion Goals more than skipping those steps would. * **Test a specific segment or flow.** Combine Holdout Split with an [Audience-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) or [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) to measure impact only for the users who matter for this campaign, instead of holding out your whole application. * **Get a per-goal uplift, not just an event count.** If the journey has several [Conversion Goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), Holdout Split reports uplift and statistical significance for each one separately. For example, place **Holdout Split** after entry and before your onboarding messages. Compare a purchase Conversion Goal for users who continued on **In journey** against users on **Holdout**, who exit there and get nothing else from this journey. ## Prerequisites Define at least one [Conversion Goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) for the journey if you want numbers in the uplift report. You can still place and save **Holdout Split** without a goal. Without one, the report shows a prompt to add a goal instead of rates and uplift. ## Add and configure Holdout Split 1. Place the **Holdout Split** element on the canvas where you want to start measuring the rest of the journey (before the messages or steps you are testing). 2. Double-click the element to open its settings. 3. Set **Share held out**: the percentage of users reaching this point who are routed to the **Holdout** branch. The rest continue on the **In journey** branch. Share held out accepts 1–20%. ![Holdout Split element configuration panel with the Share held out field set to 10 percent](https://docs.pushwoosh.com/journey-elements-holdout-split-1.webp) 4. Connect the branches on the canvas: * Leave the **Holdout** branch unconnected. Users on that branch exit the journey immediately and get nothing else from it. * Connect the **In journey** branch to the rest of your flow. 5. Click **Save**. After you save, you should see two branches from the element: **Holdout** with no next step, and **In journey** leading into the flow you connected. ![Holdout Split on the journey canvas with In journey leading to Time Delay and a message, and Holdout with no next step](https://docs.pushwoosh.com/journey-elements-holdout-split-2.webp) Note Held-out users are excluded from this journey only. They can still receive messages from your other journeys and one-time sends. ## Check the uplift report Use **Goal uplift vs holdout** to answer one question: do the steps after this point raise your Conversion Goals compared with users who stopped here? 1. Open the running journey. 2. Open the **Holdout Split** point and find **Goal uplift vs holdout** below the current split. 3. If you see a placeholder instead of rates, come back the next day. Pushwoosh refreshes the report once a day, and the first numbers appear the day after the journey starts. 4. In the report header, check sample size and timing before you trust the rates: * How many users were on **In journey** and **Holdout** at the last refresh * The attribution window (how many days after the point a goal completion still counts) * When the report was last computed A tiny holdout sample makes uplift noisy even if the in-journey rate looks strong. 5. For each Conversion Goal, compare the branches, then read uplift and significance: * Compare **Conversion, in journey** with **Conversion, holdout** (rate and count). That is the difference between users who got the rest of the journey and users who got nothing else from it after this point. * Read **Uplift** for that difference as a relative change. Positive uplift means the in-journey branch converted better than holdout. If holdout had zero conversions for this goal, uplift shows **—** (no base for a relative difference). * Read the significance badge before you treat uplift as a decision: * **Significant:** the difference between the branches is real, not random. Trust the uplift number. * **Not significant:** Pushwoosh has enough data, but the difference isn’t reliable. Don’t act on this uplift number yet. * **Not enough data:** too few conversions so far to tell either way. Keep the journey running for a larger sample. To review every Holdout Split in one place, open the journey’s [Campaign health dashboard](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#campaign-health) and check the same report for each point. To get the uplift for every Holdout Split point as a CSV file, with revenue per currency, export the [uplift report](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/) through the API. ## Export users from each branch Download a list of which users went to **Holdout** and which went to **In journey** in a running journey. **Export users** requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. 1. Open the running journey. 2. Open the **Holdout Split** point. 3. Click **Export users** below **Goal uplift vs holdout**. 4. Set the date range. You can export data from the last 2 months only. ![Holdout Split point panel with Goal uplift vs holdout and the Export users date range picker](https://docs.pushwoosh.com/journey-elements-holdout-split-3.webp) 5. Click **Start exporting**. 6. When the file is ready, a download button appears in the same panel. Click it to get the CSV. Each row lists a user ID and the branch that user took, `holdout` or `journey`. If no one reached the point in that date range, the file has only the header row. If you need an account-wide holdout instead of a per-journey one, use [Control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). --- # Loop **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/loop/ Repeat journey steps for a user up to a set number of times, with an optional pause between repeats, without copying them. --- Use **Loop** to repeat the same steps for a user several times without copying them on the canvas. It fits reminders, follow-ups, and checks that should run again and again, up to a limit you set. After the last repeat, the user moves on to the next part of the journey. ##### Use cases * **Payment reminders:** send a reminder and wait for the payment. Users who pay move on and get no more reminders. Users who don’t pay get another reminder after a pause, up to the limit you set. * **Win back inactive users:** send a limited number of nudges with a delay between them. After the last one, move users to a different offer or stop sending them messages. * **Keep checking for an event:** wait for the same event again and again without copying the wait step across the canvas. ## Prerequisites If Loop is missing from the elements list, ask [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to turn it on for your account. ## Add and configure Loop Build the steps you want to repeat first, then place the Loop right after them. The Loop has two outputs: * **Repeat:** a connection back to the first repeated step. Users go back there and go through the steps again. * **Exit:** a connection forward to what comes after the loop. Users go there after their last repeat. Example: payment reminders On the canvas, a payment reminder loop looks like this: * **Push:** sends the reminder. * **Wait for Trigger:** waits for the payment. Users who pay go down its payment branch and get no more reminders. Users who don’t pay go down **Not triggered** to the Loop. * **Loop:** **Repeat** sends users back to the **Push** for the next reminder. 1. Add the **Loop** element right after the steps you want to repeat, and double-click it. 2. In **Maximum repeats**, set how many times a user can go through the repeated steps, from 1 to 100. The default is 10. A number outside that range snaps to the nearest limit as soon as you save. 3. In **Not more often than every, s**, set the shortest pause between two repeats for one user, in seconds: * **0:** use it when the repeated steps already wait on their own, with a **Wait for Trigger** step, a **Time delay** step, or another Loop that has its own pause. * **60 or more:** use it when nothing in the repeated steps waits. The Loop holds each user until that time has passed since their last repeat. If you type a value from 1 to 59, it changes to 60 right away. If you leave `0` but none of the repeated steps waits, the journey won’t save and shows `The loop body has no waiting element, set a minimum interval`. Without any pause, the loop would repeat as fast as the system can process it. Set 60 or more, or add a **Wait for Trigger** or **Time delay** step to the repeated steps. ![Loop configuration drawer with Maximum repeats set to 10 and Not more often than every, s set to 0](https://docs.pushwoosh.com/journey-elements-loop-1.webp) 4. Drag a connection from the **Repeat** output back to the first step you want to repeat. In the example above, that’s the reminder **Push**. If **Repeat** isn’t connected, the journey won’t save and shows `Connect the repeat output of the Loop`. If it leads to the Loop itself, you’ll see `The repeat output of the Loop cannot lead to the Loop itself`. 5. Drag a connection from the **Exit** output to the step that should come after the loop, for example a final message or a different offer. Users go there once they’ve gone through the loop **Maximum repeats** times. ![Canvas: Push, Wait for Trigger with a Trigger group 1 output, Loop. Not triggered goes to Loop, Repeat back to Push, Exit forward](https://docs.pushwoosh.com/journey-elements-loop-2.webp) 6. Click **Save**. Only **Repeat** can point back to an earlier step. If you connect any other output to a step earlier in the journey, the journey won’t save. ## What happens on each repeat Every step in the repeated part runs again each time. Keep these rules in mind when you build it. ### Tag changes add up An [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) step inside the loop runs on every repeat. What that means depends on the action: * **Increase, decrease, add a list item, remove a list item:** the change adds up. For example, with **Maximum repeats** set to 100, a Tag increased by 1 grows by 100, not by 1. * **Replace the value:** safe to repeat, because it doesn’t add up. When the step has a change that adds up, it shows a warning explaining that the change adds up on every repeat. ![Update user profile step inside a loop with an increment action and the repeat multiplier warning shown](https://docs.pushwoosh.com/journey-elements-loop-3.webp) ### Waits start over on each repeat A **Wait for Trigger** or **Time delay** step counts its time from the current repeat, not from when the user entered the loop. A 24-hour wait means 24 hours on every repeat, so a user who goes through the loop 3 times can spend up to 72 hours in it. ### Users stay in the same test branch An [A/B/n Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) sends each user to the same branch every time they reach it: on every repeat, and when they enter the journey again later. A [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) works the same way. This keeps test results accurate, because no user can move to another branch and be counted twice. ### Wait for Trigger uses the latest event A **Wait for Trigger** step in the loop keeps only the event from the current repeat. Each new event replaces the one from the previous repeat. Later steps that use event data always get the latest event, including message placeholders, a dynamic **Time delay**, a dynamic Tag value, and a **Condition split**. ### Goals count once, messages count every time A [Conversion Goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) reached inside the loop counts once for the journey, not once per repeat. Messages work differently. Each repeat of a channel step is a separate send and counts toward the user’s [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) limit. A **Push** step that repeats 10 times can use up to 10 sends of that limit, not one. Tip To make sure a user doesn’t get more messages from a step than the channel’s limit allows, turn on frequency capping in every [channel step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/) inside the loop. Set the limit in one of these ways, or the step stays unlimited: * In [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). * With a per-step custom cap. ## Nested loops You can put one Loop inside the repeated part of another. Each Loop counts its own repeats. The inner Loop starts counting from zero each time users leave it, so it runs all its repeats again on every repeat of the outer Loop. For example, if the outer Loop’s **Maximum repeats** is 3 and the inner Loop’s is 2, the inner Loop’s steps can run up to 2 × 3 = 6 times. The [combined limit of 1000 repeats](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/loop/#safety-limit-across-all-loops) still applies. ## Safety limit across all loops One user can go through at most 1000 repeats in total across all Loops in a journey, even if each Loop’s own **Maximum repeats** allows more. * A user who reaches 1000 leaves through the last Loop’s **Exit**, even if that Loop’s own limit isn’t reached yet. * The limit protects against loops inside loops multiplying into far more repeats than you expect, for example two 100-repeat loops placed one inside the other. --- # Reachability check **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/ Send users to the first channel they're reachable on, from push and email to SMS, WhatsApp, LINE, in-app, and wallet pass. --- The **Reachability check** element tests a user against a list of channels you choose and sends them down the branch for the first channel they’re reachable on. Add channels in priority order (push, email, SMS, WhatsApp, LINE, in-app, and wallet pass are all available) and route the rest to a fallback branch. Use it to try a preferred channel first and fall back to alternatives instead of losing users who aren’t reachable on it. ##### When to use * You want to send a push first, then email or SMS only if the user isn’t reachable on push. * You want one path for users with a wallet pass and another path (email or in-app) for everyone else. * You want to message users by email when they have it, fall back to SMS if they don’t, and use **All others** for everyone left. * You want to email an app invitation to users who don’t have your mobile app yet. See [Invite users to install the app](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/#invite-users-to-install-the-app). ## Add channels to check 1. Drag the **Reachability check** element onto the canvas from the **Flow** section of the left panel. 2. Double-click the element to open the configuration panel. 3. In **Step Name**, enter a name for the point, for example `Reachability check for push campaign`. This name replaces the default label on the canvas. 4. Under **Add a channel**, click each channel you want to check. Repeat until your list is complete. 5. For **Push Notifications**, **In-Apps**, and **Wallet Pass**, choose which platforms to include. A newly added channel starts with **All platforms** selected. Keep that, or pick only the ones you need: * **In-Apps** and **Wallet Pass:** **iOS**, **Android** * **Push Notifications:** **iOS**, **Android**, **Web**, **Desktop**, **Amazon** 6. Use the **Move up** and **Move down** arrows next to a channel’s name to change its position in the list, or the trash icon to remove it. ![Reachability check panel with Push, Email, and SMS in priority order and platform chips visible](https://docs.pushwoosh.com/reachability-check-3.webp) 7. Under **Which devices to check**, choose whether the check looks at **All devices of the user** or **Only the device that entered** the journey. 8. Click **Save**. ## Choose which devices to check A user can have several devices. For example, they might enter the journey on an Android phone but also own an iPad. By default, the check looks across all of the user’s devices for a channel, so a user who entered on Android can still match a channel or platform tied to a different device. Under **Which devices to check**, choose **All devices of the user** to keep that behavior, or **Only the device that entered** to check the entry device only, so an Android traveler isn’t matched into an iOS branch for example. **All devices of the user** covers the devices that share the user’s [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) in the journey’s application. ![Which devices to check set to Only the device that entered, Push Notifications narrowed to Android](https://docs.pushwoosh.com/reachability-check-6.webp) The entry device isn’t limited to a push-registered one. **Only the device that entered** checks whichever device is tied to how the user entered the journey, including an email- or SMS-only device, or the specific device matched by an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). If you leave both options unselected, Pushwoosh falls back to your account’s default scope, named in the hint under the radio buttons (for example, “this account checks only the device that entered by default”). Points built before this update keep that same default. Nothing is preselected for them. The chosen scope also shows in the read-only view of a running journey version, as a line under the branch list. Points using the account default don’t show this line. ![Read-only Reachability check panel showing Checked on the device that entered the journey](https://docs.pushwoosh.com/reachability-check-7.webp) Condition split’s Segment and Tag strategies use the same **Which devices to check** control. See [Choose which devices to check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#choose-which-devices-to-check) on that page for how it behaves there. ## Connect the branches After you save, each channel becomes its own branch on the canvas, in the same order as the list. Users who don’t match any listed channel go down **All others**. Each branch is labeled with its channel name. Push Notifications, In-Apps, and Wallet Pass use short labels on the canvas: **Push**, **In-App**, and **Wallet**. If you narrowed a channel to specific platforms, the label lists them after the channel name, for example **Push: iOS** or **Wallet: iOS, Android**. A channel left on **All platforms** shows the channel name only. 1. Connect the next step to each channel branch. For example, put a Push step on **Push** and an Email step on **Email**. 2. Connect **All others** to the path you want for everyone else. ![Example journey with Reachability check branches connected to Push, Email, SMS, and a fallback path](https://docs.pushwoosh.com/reachability-check-5.webp) If you remove a channel later, only that branch is deleted. Every other branch keeps the connection it already had. ## Which branch the user follows Pushwoosh checks the channels in the order you set in the list. The user follows only the first channel they are reachable on, then continues along that branch. If they aren’t reachable on any of those channels, they go down **All others**. Only a user who has no device at all in the application doesn’t go down any branch: they drop out of the journey at this element with the [Device not found](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#device-not-found) reason, for example when the entry event came with a User ID that has no devices. Example You add channels in this order: **Push Notifications**, then **Email**, then **SMS**. A user with push enabled goes down the Push branch and never reaches Email or SMS in this check. A user without push but with an email address goes down the Email branch. A user with none of these channels goes down **All others**. What counts as reachable depends on the channel: | Channel | User is reachable when | | -------------------- | ------------------------------------------------------------------------------------------------------------------- | | Push | A device is registered on a matching platform, and the app’s **Push Alerts Enabled** tag is `true` for that device. | | Email | A device is registered, and the app’s **Unsubscribed Emails** tag is not `true` for that device. | | In-Apps, Wallet Pass | A device is registered on a matching platform (a pass is installed, for Wallet Pass). | | SMS, WhatsApp, LINE | A device is registered for that channel. | None of the channels check push-token validity or the OS-level notification permission, only the tag or device state above. ## Invite users to install the app Use the check to find users who don’t have your mobile app yet and email them an invitation to install it. Caution The check sees a user’s mobile device only when their mobile, web, and email devices share the same [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) in the same application. If they don’t, a user who already has the app still receives the invitation. 1. Under **Which devices to check**, choose **All devices of the user**. 2. Add **In-Apps** with only **iOS** and **Android** selected, then **Email**. 3. Connect the **In-App: iOS, Android** branch to the next step for users who already have the app. 4. Connect an Email step with your app invitation to the **Email** branch. Users without an iOS or Android device but with a subscribed email address go here. 5. Connect **All others** to the path for users who have neither the app nor a subscribed email address. The **In-App: iOS, Android** branch matches any registered iOS or Android device, whatever its push status. To route by push status instead, add **Push Notifications** with **iOS** and **Android** first. A user who installed the app but turned push notifications off then goes down the **Email** branch and gets the invitation. Older single-channel points If you already have a **Reachability check** that checks only one channel, it still works as before: one channel branch and **All others**. You don’t need to change anything. The first time you save that point, it turns into the multi-channel list with that one channel as the only entry. --- # Condition split **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/ Use Condition split to branch journeys based on Segments, Tag values, or Event attributes. Branches can compare against a constant, another tag, or an event attribute, enabling separate paths for different user groups and personalized content delivery. --- The **Condition split** element lets you split a flow so that users follow different paths depending on their segment or tag values. ## Define the user flow You can split the flow in two ways: * **Segment**: Split users based on whether they match an existing segment. * **Tag**: Split users into multiple branches based on tag values. * **Event**: Split users into branches based on values of an event attribute. ![The Condition split element showing the options to split a flow by Segment or Tag](https://docs.pushwoosh.com/journey-elements-condition-split-1.webp) ### Split flow by segment To split a flow by segment: 1. Select **Segment** as the condition for the user flow. 2. Choose the segment you want to use, or create it from scratch right from the element. [Learn more about creating Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments) 3. Click **Save** to apply the changes. ![Condition split branches created by segment](https://docs.pushwoosh.com/journey-elements-condition-split-2.webp) 4. The flow will automatically create two branches: * **Users in a segment**: users who match the selected segment. * **All other users**: users who do not match the segment. Configure a separate flow for each branch so that users receive content relevant to them. **Example** If you split the flow by the segment *Premium subscribers*, users in this segment can be routed to a flow that delivers a loyalty discount, while all other users follow a flow that shows an upgrade prompt. ![Example of Condition Split flow for Premium vs. other users](https://docs.pushwoosh.com/journey-elements-condition-split-3.webp) ### Split flow by Tag To split a flow by Tag: 1. Select **Tag** as the condition type. 2. From the **Tag** dropdown, choose the tag you want to use (for example, *Favourite_category*). Ensure the required tag is created in advance. [Learn more about tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) 3. Select the **Condition** (for example, *is*). 4. Add one or more branches by specifying tag values (for example, *Shoes*, *Dresses*, *Accessories*). Tip For **Date** tags, the **Condition** dropdown also offers relative-date and anniversary conditions: `during the last N days`, `days ago`, `N or more days ago`, `between N and M days ago`, `between N and M days ahead`, `minutes ago`, `anniversary is today`, `anniversary is in N days`, `anniversary was N days ago`. See [Date tag operators](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#date-tag-operators) for what each condition matches — there, `days ago` is `exactly N days ago` and `anniversary was N days ago` is `is N days past`. Anniversary conditions allow up to 2 branches instead of the usual 10. For **List** tags, the **Condition** dropdown also offers conditions that count how many values the tag holds, instead of checking which values it holds: `number of values is`, `number of values is not`, `number of values is less or eq`, `number of values is greater or eq`. Each branch takes a number, not a value. For example, `number of values is greater or eq` with `3` matches users who have 3 or more values stored in the tag. `number of values is` allows the usual 10 branches. The other three count conditions allow up to 2 branches, the same restriction as Date anniversary conditions. > You can create up to **10 branches** per condition split. 5. Click **Save** to apply the changes. ![Condition Split branches created by tag values](https://docs.pushwoosh.com/journey-elements-condition-split-4.webp) 6. To compare the tag against another tag or an event attribute instead of a fixed value, see [Compare branch values](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#compare-branch-values). 7. Finally, create a separate flow for each branch to deliver personalized content based on the user’s tag. **Example** If you split the flow by the *Favourite_category* tag and define branches for *Shoes, Dresses,* and *Accessories*, users will be routed to the branch that matches their preference. All other users (for example, those without a specified category) will be routed to the **All other users** branch. You need to create a separate flow for each branch, such as promotions for shoes, dresses, or accessories, to deliver personalized content. ![Example of Condition Split flow for favourite clothing categories](https://docs.pushwoosh.com/journey-elements-condition-split-5.webp) ### Split flow by Event Use this option when you want to branch the journey based on the value of an event attribute collected earlier in the journey. To split a flow by event attribute: 1. Select **Event** as the condition type. 2. In the **Event from Journey** dropdown, choose the event triggered earlier in the journey. 3. Under **Attribute**, select the event attribute. 4. For **Date** attributes, a **Date frame** dropdown appears next to Attribute: `Specific date` (default — pick an exact calendar date, same as before), `Days ago`, or `Days ahead`. Changing the attribute resets Date frame back to `Specific date`. Tip A relative Date frame narrows the **Condition** dropdown to what Segment Builder supports for relative dates: **Days ago** offers `less or eq`, `is`, `greater or eq`, and `between`; **Days ahead** offers only `between`. The value field becomes a day count instead of a date picker — for `between`, two day counts (for example, `10` and `15` for “10 to 15 days ahead”). Switching Date frame resets the condition and any branches you already added. 5. Choose the condition logic, such as `is`, `is not`, `is one of`, `is not one of`, etc. 6. Add one or more branches and specify a value for each. The value field’s label and how you fill it in depend on the operator: | Operator | Value field label | How to fill it in | | --------------- | ---------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `is` | **Who has value** | STRING attributes: search suggested values based on previously recorded data as you type, or type your own value directly. Other attribute types: type the value directly. | | `is not` | **Who does not have value** | Same as `is`. | | `starts with` | **Who has value starting with** | Same as `is`, STRING attributes only. | | `ends with` | **Who has value ending with** | Same as `is`, STRING attributes only. | | `contains` | **Who has value containing** | Same as `is`, STRING attributes only. | | `is one of` | **Who has any of** | Search suggested values based on previously recorded data, for any attribute type. Type your own value and press Enter to add it as a separate branch value. | | `is not one of` | **Who does not have any of** | Same as `is one of`. | | `greater or eq` | **Who has value greater or equal** | INTEGER, DECIMAL, and DATE attributes only: type the value directly. | | `less or eq` | **Who has value less or equal** | Same as `greater or eq`. | Picking a suggestion always fills the field with that suggestion’s full value — for `contains`, that means the full matching value, not just the substring you typed. The suggestion list is empty until Pushwoosh has recorded values for the selected event and attribute, which is expected for a newly created event or attribute. 7. To compare the attribute against a tag or another event attribute instead of a typed value, see [Compare branch values](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#compare-branch-values). 8. Click **Save** to apply the configuration. Tip You can define up to 10 branches per Condition Split. ![Condition Split branches created by event attribute](https://docs.pushwoosh.com/journey-elements-condition-split-6.webp) **Example** If you split the flow based on the event `WorkoutTypeSelected` and the attribute `type`, you can route users to personalized onboarding flows depending on their selected workout: * Users with `type = cardio` → receive cardio workout tips * Users with `type = strength` → receive a strength training plan * Users with `type = yoga` → receive a guided yoga session * Users who did not select a workout type (All others) → receive a push message with generic sports tips, ensuring they still get relevant onboarding content even without a specific preference. Each branch then contains targeted message elements that deliver relevant content. ![Example of condition split flow branches by event attribute](https://docs.pushwoosh.com/journey-elements-condition-split-7.webp) ## Choose which devices to check Available for **Segment** and **Tag** splits. It doesn’t apply to **Event** splits, which compare a value already captured from a specific event rather than device data. A user can have several devices. For example, they might enter the journey on an Android phone but also own an iPhone. By default, Segment and Tag checks look across all of the user’s devices, so a user who entered on Android can still be routed by data tied to their iPhone. Under **Which devices to check**, choose **All devices of the user** to keep that behavior, or **Only the device that entered** to check the entry device only. ![Condition split Which devices to check control with All devices of the user selected on a Segment split](https://docs.pushwoosh.com/journey-elements-condition-split-11.webp) The entry device isn’t limited to a push-registered one. **Only the device that entered** checks whichever device is tied to how the user entered the journey, including an email- or SMS-only device, or the specific device matched by an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). If you leave both options unselected, Pushwoosh falls back to your account’s default scope, named in the hint under the radio buttons. Points built before this update keep that same default. Nothing is preselected for them. Switching between **Segment** and **Tag** keeps your choice. The chosen scope also shows in the read-only view of a running journey version, as a line under the branch list. Points using the account default don’t show this line. ![Read-only Condition split panel showing Checked on the device that entered the journey](https://docs.pushwoosh.com/journey-elements-condition-split-12.webp) Reachability check uses the same **Which devices to check** control. See [Choose which devices to check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/#choose-which-devices-to-check) on that page for how it behaves there. ## Compare branch values Each branch condition checks a selected field against a comparison target. The target can be a fixed value you define, another field from the same user’s profile, or a value captured from an earlier step in the journey. Which option you choose determines how dynamic the comparison is: a fixed value applies the same rule to everyone, while a tag or event attribute evaluates each user against their own data. Next to the value field, choose one of three comparison modes: | Mode | When to use | Example | | ------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------- | | **Value** | You decide a fixed target. The same value applies to every user. Use when one answer works for the whole audience. | Send a shoes promo only when `Favourite_category` is `Shoes`. | | **Tag** | The target is another field on the same user’s profile. Each user is compared against their own data. Only tags of a compatible type appear in the list. | Send a “ready to redeem” message only when `integer_tag` is greater or equals that user’s `Points`. | | **Event attribute** | The target comes from something the user did earlier in this journey. Select the event, then the attribute. Only type-compatible attributes are listed. | From abandoned cart, check if `totalPrice` is greater or equals the user’s `Free_shipping_threshold` tag. | Dynamic comparison is available for the following operators: * **Tag branches:** `is` and `is not` for any tag type except LIST; `greater or equals` and `less or equals` for INTEGER, DECIMAL, and DATE tags; `is one of` / `is not one of` for LIST tags. The LIST count conditions (`number of values is`, `number of values is not`, `number of values is less or eq`, `number of values is greater or eq`) always take a typed number. Tag and Event attribute targets aren’t available for them. * **Event branches:** `is` and `is not` for any attribute type except LIST; `greater or equals` and `less or equals` for INTEGER, DECIMAL, and DATE attributes; `is one of` / `is not one of` for LIST attributes. Note If the tag or event attribute you compare against is empty for a user, that branch does not match them. They go to **All other users**. A date compared with another tag or event attribute can’t take an offset: you can check whether one date `is`, `is not`, `greater or equals`, or `less or equals` another, but not, for example, “3 days before the event date”. For a condition relative to today, such as “more than 3 days ago”, use a typed value with **Days ago** or **Days ahead**. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#split-flow-by-event) ### Example scenarios #### Tag compared to another tag **Use case:** reward or unlock content only when a user’s score reaches a personal target stored on their profile. **Why:** a fixed threshold (for example, greater or equals `100`) treats everyone the same. Comparing `integer_tag` to each user’s own `Points` tag routes people based on their individual target. **How to set it up:** 1. Select **Tag** as the condition type. 2. From the **Tag** dropdown, choose `integer_tag`. 3. Set the condition to **greater or equals**. 4. For the branch value, choose **Tag**, then select `Points`. Users whose `integer_tag` is greater than or equal to their own `Points` follow this branch. Everyone else goes to **All other users**. ![Condition Split branch comparing an integer tag to another tag](https://docs.pushwoosh.com/journey-elements-condition-split-8.webp) #### Tag compared to an event attribute **Use case:** after a purchase or cart event, decide whether the user’s stored score still covers the amount from that step. **Why:** the amount is different for each session. Comparing the profile tag to the event’s `totalPrice` uses the real value from earlier in the journey instead of guessing a fixed value. **How to set it up:** 1. Select **Tag** as the condition type. 2. From the **Tag** dropdown, choose `integer_tag`. 3. Set the condition to **greater or equals**. 4. For the branch value, choose **Event attribute**, then select the trigger event and the `totalPrice` attribute. Users whose `integer_tag` is greater than or equal to the `totalPrice` from that earlier event follow this branch. Everyone else goes to **All other users**. ![Condition Split branch comparing an integer tag to an event attribute](https://docs.pushwoosh.com/journey-elements-condition-split-9.webp) #### Event attribute compared to a tag **Use case:** after abandoned cart, check whether the cart already qualifies for free shipping or a bonus, then send the matching recovery message. **Why:** cart total comes from the journey event. The free-shipping threshold can differ per user and is stored on the profile as the `Free_shipping_threshold` tag. Comparing `totalPrice` to each user’s own `Free_shipping_threshold` puts qualified carts on one branch (for example, “complete checkout for free shipping”) and the rest on another (for example, “add more to unlock free shipping”). **How to set it up:** 1. Select **Event** as the condition type. 2. From **Event from Journey**, choose `PW_AbandonedCart`. 3. Under **Attribute**, select `totalPrice`. 4. Set the condition to **greater or equals**. 5. For the branch value, choose **Tag**, then select `Free_shipping_threshold`. Users whose abandoned-cart `totalPrice` is greater than or equal to their `Free_shipping_threshold` follow this branch. Everyone else goes to **All other users**. ![Condition Split branch comparing an event attribute to a tag](https://docs.pushwoosh.com/journey-elements-condition-split-10.webp) --- # Time delay **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/ Add Time Delays in journeys to schedule actions by fixed times, dates, or user data for precise, timed communication delivery. --- The **Time Delay** element makes users wait for a specified time. After the delay, they move to the next step. The delay can be a fixed period, a specific time, or a date. It can also be based on a tag value or event attribute. ## Delay options ### Fixed duration When set to a fixed period, the Time Delay element lets users continue their journey only when the specified amount of time passes. For example, if a delay is set to 8 hours, a user who reaches this journey element will wait for 8 hours before moving to the next step. Note Please take into consideration that the delay period **cannot be longer than 30 days.** ![The Time Delay element configured to a fixed duration, showing a user waiting for a specified period before proceeding.](https://docs.pushwoosh.com/journey-elements-README-32.webp) ### Specific time You can set an exact time for users to move forward. Users who reach the delay element will proceed at the specified time. Caution The specific time option is set to the user’s timezone. For example, if a user gets to a delay element early in the morning and you have set the delay to wait until 5:30 PM, those users will proceed to the next journey point at 5:30 PM according to their device’s timezone. Note If the time you specify has already passed for some users in their timezones, those users will wait till that same exact time the next day. ![A user's journey is paused by the Time Delay element until a specific time is reached in their local timezone.](https://docs.pushwoosh.com/journey-elements-README-33.webp) ### Date If you want to set up a one-time campaign on a specific date (for example, send a Black Friday notification), select a particular date and time to continue the journey. Caution Please remember that the date and time are set according to the user’s timezone. If the date and time you specify have already passed for some users in their timezones, they will immediately proceed to the next journey element. ![A Time Delay element set to a specific date, ideal for scheduling one-time campaigns like holiday promotions.](https://docs.pushwoosh.com/journey-elements-README-34.webp) ### Day of week If you want the user to move to the next journey point only on a specific day of the week, select the **Day of week** option and set the desired day and time. Caution Please remember that the day and time are set according to the user’s timezone. If the day and time you specify have already passed for some users in their timezones, they will wait till that same day and time next week. ![The Time Delay element configured to a specific day of the week, allowing for weekly recurring schedules.](https://docs.pushwoosh.com/journey-elements-README-35.webp) ### Delay based on user or event data Use this option when the wait time should come from a Tag, an Event attribute, or a value mapped from a [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response) response. 1. In **Time delay option**, select **Based on user/event data**. 2. Under **Get date from**, select **User profile** to use a Tag, or **Event** to use an event attribute or a value from a webhook response. 3. If you selected **User profile**, choose the Tag that contains the date. 4. If you selected **Event**, choose the event and the attribute that contains the date. For webhook-mapped values, see [Use a date from a webhook response](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response) below. 5. Set the offset: **Before**, **After**, or **On** the date, then enter the number of days. ![A Time Delay based on user or event data, enabling dynamic and personalized scheduling within a Customer Journey.](https://docs.pushwoosh.com/journey-elements-README-36.webp) Date format requirements * **Tags:** use a Tag of the [Date type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#types-of-tags) and [set its values](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) in `2006-01-02T15:04:05` format (in **UTC**). * **Events:** [create an Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel), [add a Date attribute](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#2-set-attributes), and send [/postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent) requests with dates in `2006-01-02 15:04:05` format. Caution If the **date or time has already passed** when a user reaches this element, they exit the journey. #### Use a date from a webhook response If a [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) element earlier in the journey has **Response mapping** configured, mapped attributes are available under **Event**. 1. In **Time delay option**, select **Based on user/event data**. 2. Under **Get date from**, select **Event**. 3. In **Event**, select the webhook step by the name you gave it (for example, `Get appointment details`). 4. In **Event value**, select the mapped attribute that holds the date (for example, `visit_date`). 5. Set the offset (for example, **Before** `2` **Days**). ![Time Delay set to Event Webhook with visit_date and 2 days before offset](https://docs.pushwoosh.com/journey-elements-time-delay-1.webp) The value must follow the same date format rules as Tags and Events (see **Date format requirements** above). Caution If the mapped value is missing or not a valid date, the user exits the journey with an error at this step. Run [**Test webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#test-the-webhook) before launch to confirm your mapping. ### Split branches when the date is in the past or empty To let users continue when they don’t meet the delay condition, enable **Split to branches if the date’s in the past or date is empty**. The flow splits into **In the future** and **In the past**. Use the **In the past** branch for users whose Tag values, Event attributes, or webhook-mapped dates don’t match the delay. You can add other elements on that branch (for example, another **Time Delay**, **Condition split**, **Wait for Trigger**, or a message). For example, you set a reminder 2 days before an appointment using the appointment event’s date attribute. If the appointment is tomorrow, the user won’t meet the 2 days before condition and exits unless you enable branch splitting. ### When appointment dates change mid-journey If a date in a Tag or Event attribute changes while the user is already in the journey, **Time Delay** settings stay as they were when the user reached the element. Consider creating several journeys when users often reschedule appointments, deliveries, or similar events. For example, start a journey on the `AppointmentCreated` event with a DateTime attribute (a Reminder journey). Set a push reminder 2 days before the visit using **Based on user/event data**. To handle reschedules: 1. Create an `AppointmentChanged` event. 2. In the Reminder journey, set `AppointmentChanged` as a conversion goal so users who reschedule exit the journey. 3. Create a new journey that starts on `AppointmentChanged` to remind users who updated their visit date and time. ## Pausing a journey while a user is waiting Pausing a journey does not pause the Time Delay timer: the send time is fixed when the user enters the delay point, regardless of the journey’s pause state. * If that time falls inside the pause window, the user waits at this step and the message goes out immediately once you resume the journey. * If it falls after the resume, the message goes out at the originally scheduled time, as if the pause never happened. A user waiting at this point is lost if you pause the journey for longer than 7 days (this happens silently, with no log entry or metric), if you select **Stop campaign** while the journey is paused, or if you delete this element during editing. See [Editing an active journey](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#editing-an-active-journey) for what else can happen to users when you pause a journey. --- # Update user profile **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/ Update user profiles with manual or event-based Tag values to personalize journeys and enhance targeting in Pushwoosh. --- The **Update user profile** element assigns Tag values to users as they move through a journey. You can set values manually or pull them from Event attributes or a [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) response. ## Update options ### Manual update To assign Tag values to users within the journey manually: * Add the **Update user profile** element wherever you’d like on the canvas. * Press **+ Manual value** and select a Tag from the drop-down list containing all the Tags created in your Pushwoosh account. * Specify values for the Tag selected depending on its type. You can set up to 10 Tags at once. For user-specific Tags, values are assigned to all users’ devices with the same user ID. For Tags that are not user-specific, values are assigned to a particular device a user is traveling the journey with. As for use cases, there are plenty. For example, tagging users who reach a particular journey stage comes in handy when building further communications. ![The Update user profile element showing manual tag assignment options in the Customer Journey builder.](https://docs.pushwoosh.com/journey-elements-README-37.webp) ### Dynamic tag value Dynamic update means Tag values are taken automatically from Event attributes that users trigger before they reach the **Update user profile** element. You can communicate to those users more personally and send them tailored offers. Note To learn more about Events and their attributes, please refer to the [Events guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). To set dynamic Tag values from an Event: 1. Place the **Update user profile** element on the canvas after at least one trigger-based element (**Trigger-based Entry** or **Wait for Trigger**). ![The Update user profile element positioned after a trigger-based element in a Customer Journey flow.](https://docs.pushwoosh.com/journey-elements-README-38.webp) 2. Double-click the element and press **+ Dynamic Value** in the **Dynamic Tag Value** section. 3. Select the Tag to set. Caution If you choose a Tag to be set manually, it won’t appear in the Tags list of the **Dynamic Tag Value** section. 4. Choose an Event and the attribute that supplies the Tag value. Caution Make sure the Tag and Event attribute types match. For example, to set a STRING Tag, use a STRING Event attribute. To learn more about Tag types, please refer to the [Tags and Filters guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#types-of-tags). ![The Dynamic Tag Value configuration showing event selection and tag mapping options.](https://docs.pushwoosh.com/journey-elements-README-39.webp) #### Use a value from a webhook response If a [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) element earlier in the journey has **Response mapping** configured, it appears in the **Event** dropdown by the webhook step name you set (for example, `Create user in CRM`), alongside regular Events. For a full journey example, see [Using webhook response data in your journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/). 1. Place **Update user profile** after the **Webhook** step. 2. In **Dynamic Tag Value**, press **+ Dynamic Value**. 3. In **Tag**, select the Tag to update. 4. In **Event**, select the webhook step by the name you gave it (for example, `Create user in CRM`). 5. In **Dynamic Value**, select the mapped attribute (for example, `subscription_status`). ![Dynamic Tag Value with Tag, Event set to Webhook step name, and Dynamic Value from response mapping](https://docs.pushwoosh.com/journey-elements-update-user-profile-1.webp) Caution If a webhook-mapped value is missing for a user (the response wasn’t JSON, the **Path** didn’t resolve, or the webhook call failed), Pushwoosh skips that Tag update. The Tag keeps its previous value or stays unset, and the journey continues with no error at this step. This differs from [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response), which exits the user with an error when the value is missing or not a valid date. Run [**Test webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#test-the-webhook) before launch to confirm your mapping. [Using webhook response data in your journey ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/) --- # Wait for Trigger **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/ Trigger actions based on user events with up to 4 conditions per branch and a 90-day wait time in customer journeys. --- The **Wait for Trigger** element allows you to control how your journey flows based on whether a user triggers one or more specific events within a set timeframe (up to 90 days). You can create up to three branches, each defined by a specific event or set of events. This allows you to design different communication paths based on which event(s) the user triggers. There is also an additional branch, called *Not triggered*, for users who don’t meet the conditions of any other branch. ![Wait for Trigger element on the journey canvas showing event-based branching](https://docs.pushwoosh.com/journey-elements-README-23.webp) ##### Use cases * Sending a special discount to users who don’t complete a purchase within a set period after adding items to cart. * Creating separate communication paths based on which action a user takes after receiving a push notification: opening the notification, clicking a link in the notification, or taking no action. * Setting up different follow-up messages depending on whether a user views a product, adds it to cart, or purchases it within a set timeframe. ## Set the waiting period After adding the Wait for Trigger element, specify how long the system should wait for the user to trigger your chosen events. You can set the waiting period for up to 90 days. If none of the selected events are triggered within this timeframe, the user will continue down the **Not triggered** branch. ![Configuration interface showing the waiting period settings for the Wait for Trigger element with options to set duration and enable fixed waiting period](https://docs.pushwoosh.com/journey-elements-wait-for-trigger-1.webp) #### Fixed waiting period (optional) Enable **Fixed waiting period** if you want the user to always wait the full duration, even if their event happens earlier. ![Configuration interface showing the Fixed waiting period option in the Wait for Trigger element settings.](https://docs.pushwoosh.com/journey-elements-README-27.webp) Example Suppose your journey steps are: **Push** → **Wait for Trigger** → **Email**. If you set the **Wait for Trigger** step to 1 day, and turn on **Fixed waiting period**, even if the user triggers the event quickly (for example, just 5 minutes after the push), they will still wait the full 1 day before moving on to receive the email. ## Pausing a journey while a user is waiting If the waiting period ends while the journey is paused, the user waits at this step. Once you resume the journey, the user continues down the **Not triggered** branch. A user waiting at this point is lost if you pause the journey for longer than 7 days (this happens silently, with no log entry or metric), if you select **Stop campaign** while the journey is paused, or if you delete this element during editing. See [What happens to users already in the journey when you pause it](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#what-happens-to-users-already-in-the-journey-when-you-pause-it) for details. ## Configure branches Branches let you set up different outcome paths in the journey, depending on which events the user triggers. You can add up to three branches, plus there is always a **Not triggered** branch for users who don’t match any event conditions. To configure a branch: 1. Enter a name for the branch in the **BRANCH NAME** field. 2. Add events to the branch by clicking **ADD EVENT** and selecting an event from the dropdown menu. You can add up to four events per branch. 3. For each event, you can add conditions by clicking **ADD CONDITION** to filter events by specific attributes. For the available operators, see [Tag operators by type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#tag-operators-by-type). ![Configuration interface showing how to add conditions to events in a branch with the ADD CONDITION button and attribute filter options](https://docs.pushwoosh.com/journey-elements-wait-for-trigger-6.webp) 4. To remove an event from a branch, click **REMOVE EVENT**. If a branch includes multiple events, you can choose how they’re evaluated using one of the following logical operators: * **AND:** The user must trigger all listed events to proceed along this branch. * **OR:** The user needs to trigger at least one of the listed events to proceed. This lets you tailor each branch to handle complex combinations of user actions in a clear and structured way. [](https://docs.pushwoosh.com/journey-elements-README-24.webm "Configuration interface showing how to set up multiple events with AND/OR logical operators in the Wait for Trigger element.") To add a new branch, click **Add branch**. ![Configuration interface showing the Add branch button and multiple branch setup options in the Wait for Trigger element](https://docs.pushwoosh.com/journey-elements-wait-for-trigger-5.webp) Tip Before using Wait for Trigger, make sure the events you want to monitor already exist in your project. [Learn how to create events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) ### Set up multi session attribute matching If your journey uses [**Multiple active sessions per user**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#multiple-active-sessions-per-user), this section appears automatically. Its purpose is to ensure that each incoming event is matched to the correct user session, rather than applied to every active session the user may have. To configure session matching: 1. First, add an event to your branch (as described in the [Configure branches](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/#configure-branches) section above). 2. Then, select an attribute in the dropdown that identifies the session. The attribute you select must satisfy two conditions: * It must match the identifier used in the Entry step (for example, `order_id`, `appointment_id`, `transaction_id`). * It must also be present in the event you added to the branch. ![Configuration interface showing the multi-session attribute matching dropdown with session identifier selection options](https://docs.pushwoosh.com/journey-elements-wait-for-trigger-2.webp) Important You can use the exact same attribute as in the Entry step or any attribute that contains the same value (for example, an `order_id` inside `order_list`). If the value does not match the Entry session identifier, Pushwoosh will not know which session the event belongs to and will apply the event to **all** active sessions for that user. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#multiple-active-sessions-per-user) ##### Example Your app allows users to book multiple appointments. Each booking starts a separate journey session identified by an attribute such as `appointment_id`. Suppose a user books two appointments: * Appointment **1001** * Appointment **1002** This creates two active sessions, one per appointment. Later, the user triggers an event like `AppointmentConfirmed` that contains `appointment_id: 1001`. The **Wait for Trigger** element compares this value to the session identifier and correctly applies the event only to the session for appointment **1001**. This ensures the right appointment flow continues. > If the event does not include the session-matching attribute (in this case `appointment_id`), Pushwoosh cannot determine which session it belongs to. The event will then be applied to **all** active user sessions, leading to incorrect or duplicated flows. ## Example use cases **1. Set up special communications for users who trigger one or several specific events.** Imagine you want to email customers who’ve booked and paid for a plane ticket. To do the job, add a Wait for Trigger step with one branch and specify two events in it: *TicketBooked* and *TickedPurchased* (assume you configured them before). Select the *AND* logical operator so that only users who meet both conditions will proceed further. ![Example of a Wait for Trigger configuration with two events using AND logic for ticket booking and purchase.](https://docs.pushwoosh.com/journey-elements-README-25.webp) **2. Split the flow depending on the type of product purchased.** Let’s say you offer Basic and Premium subscriptions. When buying a subscription, users trigger the *SubscriptionPurchased* event with the *type* attribute that gets the *Basic* or *Premium* value. To split the journey flow depending on the subscription type, add a Wait for Trigger step with two branches. In the first branch, specify the *SubscriptionPurchased* event with the *type is Basic* condition; in the second, add the *SubscriptionPurchased* event with the *type is Premium* condition. [](https://docs.pushwoosh.com/journey-elements-README-26.webm "Example of a Wait for Trigger configuration with two branches for Basic and Premium subscription types.") --- # Integration points **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/ Connect third-party CRM and data tools as Integration points in a customer journey, then open each guide to configure the point. --- Use Integration points to pass journey data into the CRM, analytics, spreadsheet, or AI tools you already use, without building a raw [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) for each destination. Each point is a ready-made journey step for a specific tool, for example HubSpot, Google Sheets, or Amplitude. ## Available integrations | Point | When to use | | ----------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Adapty:** grant access level | When the journey should grant premium access without a purchase. For example, reward a traveler with a paid access level as compensation. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adapty-grant-access-level/) | | **Adapty:** update profile attributes | When Adapty should pick a paywall using data the journey already knows. For example, write the traveler’s segment or step reached onto their Adapty profile. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adapty-update-profile-attributes/) | | **Adjust:** send event | When you want journey activity in Adjust attribution reports. For example, send a conversion event so it reaches your ad networks. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adjust-send-event/) | | **Airtable:** create record | When the journey should store a row in Airtable. For example, add a lead or case record with fields from the traveler. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/airtable-create-record/) | | **Amplitude:** set user properties | When you need Amplitude segments to match Pushwoosh tags. For example, sync plan tier or trial status onto the Amplitude user profile. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/amplitude-set-user-properties/) | | **Amplitude:** track event | When you want journey activity in Amplitude analytics. For example, log that a welcome push was sent so it appears in Amplitude funnels. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/amplitude-track-event/) | | **AppsFlyer:** send event | When you want journey activity in AppsFlyer attribution reports. For example, send a conversion event so it reaches your ad networks. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/appsflyer-send-event/) | | **BigQuery:** insert row | When the journey should write into BigQuery. For example, insert a row for warehouse reporting or joins. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/bigquery-insert-row/) | | **Dynamics 365:** create or update record | When the journey should keep Dynamics 365 (Dataverse) in sync. For example, create or update a row matched by key. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/dynamics-upsert-record/) | | **Gemini:** generate text | When you need short AI copy mid-journey for a later message. For example, draft a line with Gemini and reuse it in a push, email, or SMS. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/gemini-generate-text/) | | **Google Sheets:** add row | When you want journey values in a spreadsheet as they happen. For example, append HWID and campaign name for a simple ops log. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/google-sheets-add-row/) | | **HubSpot:** add timeline event | When you want journey activity on a HubSpot contact’s own timeline, next to their emails and calls. For example, log that a traveler passed a step and whether they’re subscribed to push. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-add-timeline-event/) | | **HubSpot:** create or update contact | When the journey should create or refresh a contact in HubSpot CRM. For example, upsert by email after a lead fills a form. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-create-or-update-contact/) | | **HubSpot:** get contact properties | When a later step needs data already stored on a HubSpot contact. For example, read a property to print it in a message or branch on it. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-get-contact-properties/) | | **HubSpot:** update subscription | When sales or email marketing need to see a contact’s push subscription state. For example, write it onto the HubSpot contact after a journey step. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-update-subscription/) | | **LLM:** generate text | When you need short AI copy mid-journey for a later message. For example, draft a line with Claude and reuse it in a push, email, or SMS. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/llm-generate-text/) | | **Mattermost:** post message | When a journey step should notify your team in Mattermost. For example, post when a high-value conversion happens. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mattermost-post-message/) | | **Microsoft Fabric:** ingest row | When the journey should feed Microsoft Fabric analytics. For example, ingest a row into an Eventhouse table. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/fabric-eventhouse-ingest/) | | **Mixpanel:** set profile | When you need Mixpanel profiles and segments to match Pushwoosh tags. For example, sync email or plan onto the Mixpanel profile. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mixpanel-set-profile/) | | **Mixpanel:** track event | When you want journey activity in Mixpanel analytics. For example, log a step so you can use it in Mixpanel funnels next to product events. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mixpanel-track-event/) | | **OpenAI:** generate text | When you need short AI copy mid-journey for a later message. For example, draft a line with OpenAI and reuse it in a push, email, or SMS. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/openai-generate-text/) | | **Salesforce:** create or update record | When the journey should keep Salesforce in sync. For example, create or update a Contact matched by external id. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/salesforce-upsert-record/) | | **Slack:** post message | When a journey step should notify your team in Slack. For example, post when a high-value conversion happens. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/slack-post-message/) | | **Snowflake:** insert row | When the journey should write into Snowflake. For example, insert a row for reporting or a downstream pipeline. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/snowflake-insert-row/) | | **Supabase:** insert row | When the journey should write into a Supabase table. For example, insert a row your app already reads from that database. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/supabase-insert-row/) | ## Install an integration Note Only the [account owner](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/) can install a Customer Journey integration. Installing a point template and connecting a service account are two separate things, done in two different places: * **Install the template:** from the journey builder or from Settings. This makes the point available to add to a journey. * **Connect the service account:** also required for these points, each a different way. Details in each guide: * [Google Sheets: add row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/google-sheets-add-row/), [BigQuery: insert row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/bigquery-insert-row/), [Microsoft Fabric: ingest row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/fabric-eventhouse-ingest/), [Salesforce: create or update record](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/salesforce-upsert-record/), and [Dynamics 365: create or update record](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/dynamics-upsert-record/) — under **Settings > 3rd-party integrations** > **Connected services**, not under **Customer Journey Integrations**. * [HubSpot: add timeline event](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-add-timeline-event/) — through the [HubSpot integration](https://docs.pushwoosh.com/product/integrations/hubspot-integration/) installed from the HubSpot side, not **Settings > 3rd-party integrations**. ### Install from the journey builder 1. In the journey left panel, open the **Integrations** section. 2. Drag an integration that is not installed yet onto the canvas. This installs the template and adds the point to the canvas in one action. If you don’t see the **Integrations** section in the left panel at all, [contact Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new/) to request access. Once it’s on, you see the full catalog, regardless of what’s already installed. ### Install from Settings 1. Open **Settings > 3rd-party integrations**. 2. Under **Customer Journey Integrations**, find the integration and click **Install**. After you install from Settings, drag the integration from the **Integrations** section of the journey left panel onto the canvas, then configure the point. ### Manage installed integrations On an installed card under **Customer Journey Integrations**: * To remove an integration, click **Remove**. * To open the setup guide, click **Docs**. An **Installed** badge with the version means the integration is installed on the account. ![Settings 3rd-party integrations page showing Customer Journey Integrations cards with Install, Remove, and Installed badges](https://docs.pushwoosh.com/journey-elements-integrations-4.webp) ## Configure the point Open the point on the canvas and fill in its fields. Pushwoosh sends the request to the destination for you. How to fill each field is in that point’s [guide](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#available-integrations). ## Configuring list and key/value fields Some fields let you add as many rows as the destination needs, instead of filling a fixed set of boxes. There are two shapes: * **List of values:** one value per row. For example, **Columns** in [Google Sheets: add row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/google-sheets-add-row/), or **Properties to read** in [HubSpot: get contact properties](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-get-contact-properties/). * **Name and value pairs:** a name and a value per row. For example, **Properties** in [HubSpot: create or update contact](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-create-or-update-contact/), or **Fields** in [Airtable: create record](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/airtable-create-record/). Each point names this field for what it holds. When a field’s rows are values to send, like **Columns** or **Properties to write**, you can insert something about the current traveler (device ID, tag, and so on) instead of fixed text. When rows are names instead, like **Properties to read**, they take a plain identifier only. See that point’s [guide](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#available-integrations). Note If you configured one of these points before it supported a variable number of values (Google Sheets with fixed **Column 1**/**Column 2**/**Column 3** fields, or HubSpot/Airtable with a single name/value pair), opening the point pre-fills your old values into the new field. Save the point once to move it onto the current template version. Until you do, it keeps running with the version it was configured against. ## Errors and failed requests If the request to the destination fails, the traveler leaves the journey at this step. See [Request error](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#request-error). Integration points follow the same [timeouts, retries, rate limits, and endpoint cooldown](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests) as [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/). Caution **BigQuery** and **Salesforce** can answer HTTP **200** even when the write did not land. The traveler can still move on. * [BigQuery: insert row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/bigquery-insert-row/#failures-and-unexpected-responses): check the Calls log and the `bigquery_insert_error` attribute * [Salesforce: create or update record](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/salesforce-upsert-record/#failures-and-unexpected-responses): check the Calls log A few templates, like the two HubSpot points that look up a contact, declare extra branches for an answer the destination treats as a normal outcome rather than a refusal — for example a contact not found. A response that matches one of those branches isn’t a failed request: it skips retries, doesn’t cool the point down, and sends the traveler down that branch instead of dropping them. See the point’s own page for what triggers each branch. --- # Adapty: grant access level **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adapty-grant-access-level/ Turn on a paid access level in Adapty for a customer journey traveler without a purchase, granted as a reward or compensation. --- The **Adapty: grant access level** Point template turns on a paid access level in Adapty for a while without a purchase, so a journey can hand out premium as a reward or a compensation. ## Configure the point 1. Drag **Adapty: grant access level** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Adapty secret key**, enter the app’s secret key from Adapty: **App settings > General**. It’s stored masked, like a webhook secret header — a public SDK key doesn’t authorize this call, so keep it out of this field. ![Adapty grant access level drawer with Step Name and Adapty secret key fields](https://docs.pushwoosh.com/journey-elements-adapty-grant-access-level-1.webp) 4. Identify the traveler with one of these fields. Leave the other empty. An empty field is dropped from the request, not sent blank. * **Adapty profile ID (default):** keep `{{tag:adapty_profile_id}}`. The Adapty → Pushwoosh integration writes that tag when the integration is enabled. Leave **Customer user ID** empty. ![Adapty profile ID field in the Adapty grant access level drawer](https://docs.pushwoosh.com/journey-elements-adapty-grant-access-level-2.webp) * **Customer user ID:** fill this if the app passes its own user ID to Adapty through `identify()`, for example `{{device:user_id}}`. Leave **Adapty profile ID** empty. ![Customer user ID field in the Adapty grant access level drawer](https://docs.pushwoosh.com/journey-elements-adapty-grant-access-level-3.webp) 5. In **Access level**, enter the access level ID exactly as it’s spelled on Adapty’s **Access levels** page. Defaults to `premium`. ![Access level field in the Adapty grant access level drawer](https://docs.pushwoosh.com/journey-elements-adapty-grant-access-level-4.webp) 6. In **Grant for**, enter how long the access stays on, counted from the moment the traveler passes this step: a number plus a unit, for example `30m`, `12h`, or `7d`. Enter `never` for access that doesn’t expire. Defaults to `7d`. ![Grant for field in the Adapty grant access level drawer](https://docs.pushwoosh.com/journey-elements-adapty-grant-access-level-5.webp) 7. Click **Save**. Note This point doesn’t check whether the traveler already has the access level before granting it — it sends the same request every time a traveler reaches this step, regardless of their current access. To limit how often a given traveler passes this step, use [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) on the journey itself. ## Response This point doesn’t map any response data back into the journey. A failed request drops the traveler from the journey — see [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Rewarding a referral with temporary premium An app wants to reward a user with a week of premium the moment they complete a successful referral, without a purchase. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `ReferralCompleted` event. 2. Add **Adapty: grant access level** right after it. Leave **Adapty profile ID** at its default. Set **Access level** to `premium` and **Grant for** to `7d`. 3. Click **Save**. Once this runs, every traveler who completes a referral gets a week of premium access in Adapty, with no purchase involved. --- # Adapty: update profile attributes **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adapty-update-profile-attributes/ Write custom attributes onto a customer journey traveler's Adapty profile, so Adapty picks a paywall by what the journey knows. --- The **Adapty: update profile attributes** Point template writes custom attributes onto the traveler’s Adapty profile, so Adapty can pick a paywall by what the journey knows: the step reached, engagement, segment. ## Configure the point 1. Drag **Adapty: update profile attributes** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Adapty secret key**, enter the app’s secret key from Adapty: **App settings > General**. It’s stored masked, like a webhook secret header — a public SDK key doesn’t authorize this call, so keep it out of this field. ![Adapty update profile attributes drawer with Step Name and Adapty secret key fields](https://docs.pushwoosh.com/journey-elements-adapty-update-profile-attributes-1.webp) 4. Identify the traveler with one of these fields. Leave the other empty. An empty field is dropped from the request, not sent blank. * **Adapty profile ID (default):** keep `{{tag:adapty_profile_id}}`. The Adapty → Pushwoosh integration writes that tag when the integration is enabled. Leave **Customer user ID** empty. ![Adapty profile ID field in the Adapty update profile attributes drawer](https://docs.pushwoosh.com/journey-elements-adapty-update-profile-attributes-2.webp) * **Customer user ID:** fill this if the app passes its own user ID to Adapty through `identify()`, for example `{{device:user_id}}`. Leave **Adapty profile ID** empty. ![Customer user ID field in the Adapty update profile attributes drawer](https://docs.pushwoosh.com/journey-elements-adapty-update-profile-attributes-3.webp) 5. In **Custom attributes**, add one row per attribute to write onto the profile, with a name and its value. Names must be non-empty and unique. See [key/value fields](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#configuring-list-and-keyvalue-fields). * A cell that holds one macro keeps that macro’s type. For example, a numeric tag arrives as a number. * You can send up to 30 attributes. * Each name can be up to 30 characters: letters, digits, dot, dash, and underscore. * Each value can be up to 50 characters. ![Custom attributes field in the Adapty update profile attributes drawer](https://docs.pushwoosh.com/journey-elements-adapty-update-profile-attributes-4.webp) 6. Click **Save**. Note This point only writes the **Custom attributes** you list. It can’t set Adapty’s built-in profile fields (email, phone number, first name, last name) — set those through Adapty’s own SDK or dashboard. ## Response This point doesn’t map any response data back into the journey. A failed request drops the traveler from the journey — see [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Telling Adapty which paywall segment a traveler is in An app wants Adapty to show a different paywall to trial users than to lapsed subscribers, based on what the journey already knows. 1. Add **Adapty: update profile attributes** at the point in the journey where the traveler’s segment is known. Leave **Adapty profile ID** at its default. In **Custom attributes**, set `segment` to `{{tag:Segment}}`. 2. Click **Save**. Once this runs, the traveler’s Adapty profile carries their segment, and Adapty’s paywall targeting can use it the same way it uses any other profile attribute. --- # Adjust: send event **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/adjust-send-event/ Send one server-to-server event per traveler to Adjust, carrying the install attribution, from a customer journey step. --- The **Adjust: send event** Point template sends a server-to-server event to Adjust for every traveler that passes this step. The event carries the attribution of the install, so the journey step shows up in Adjust reports and can be handed to ad networks as a conversion. ## Configure the point 1. Drag **Adjust: send event** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Adjust endpoint**, pick the region the Adjust account stores its data in: `s2s.adjust.com` for the US region, `s2s.eu.adjust.com` for the EU one. ![Adjust send event drawer with Step Name and Adjust endpoint fields](https://docs.pushwoosh.com/journey-elements-adjust-send-event-1.webp) 4. In **App token**, enter the app token from Adjust: **Settings > your app**. One app token belongs to one Adjust app, so a cross-platform journey needs a separate point per app. ![App token field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-2.webp) 5. In **S2S security token**, enter the token from Adjust: **App Settings > Protocol settings**. Pushwoosh sends it as the request’s Authorization header. It’s stored masked, like a webhook secret header, and Adjust never returns it once saved. ![S2S security token field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-3.webp) 6. In **Event token**, enter the token of the Adjust event this step reports, from Adjust: **Events**. The event must already exist there. ![Event token field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-4.webp) 7. In **Identifier type**, pick which identifier Adjust matches the event by: `idfv` or `idfa` on iOS, `gps_adid` or `android_id` on Android, `adid` for the Adjust device ID on either platform. ![Identifier type field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-5.webp) 8. In **Identifier value**, keep the default `{{device:hwid}}` (the IDFV on iOS and the Android ID on Android), or use a tag for the rest, for example `{{tag:GAID}}` or `{{tag:AdjustID}}`. ![Identifier value field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-6.webp) 9. In **Environment**, keep `production`, or pick `sandbox` to keep the event out of Adjust’s reports while the point is being set up. ![Environment field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-7.webp) 10. Optional. In **Revenue**, add the amount from a tag or an event attribute, for example `{{tag:LastPurchaseAmount}}`. Leave it empty for an event that carries no money. ![Revenue field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-8.webp) 11. In **Currency**, enter the ISO 4217 code of the revenue, for example `USD`. Required if **Revenue** is filled in, because Adjust refuses revenue without a currency. ![Currency field in the Adjust send event drawer](https://docs.pushwoosh.com/journey-elements-adjust-send-event-9.webp) 12. Click **Save**. ## When no request is sent Pushwoosh skips the request (and the traveler continues to the next step) when: * **Identifier value** resolves to empty for that traveler * The traveler is on a platform Adjust doesn’t measure: anything other than iOS, Android, Huawei Android, Baidu Android, Amazon, OSX, or Windows That isn’t a failed request. Adjust would have nothing to match the event to. A failed request (one that did have an identifier) drops the traveler from the journey instead. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Reporting a completed purchase to Adjust An app wants a purchase made later in a journey to carry the install’s attribution in Adjust, so it can be handed to ad networks as a conversion. 1. Add **Adjust: send event** right after the step that confirms the purchase. Set **Event token** to the token of the `purchase` event from Adjust’s **Events** page. In **Revenue**, set `{{tag:LastPurchaseAmount}}`, and in **Currency**, set `USD`. 2. Click **Save**. Once this runs, the purchase shows up in Adjust’s reports with the install’s attribution, ready to hand to ad networks as a conversion. --- # Airtable: create record **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/airtable-create-record/ Append a record with any number of fields to an Airtable table from a customer journey step. --- Use **Airtable: create record** to append a new record to an Airtable table from a journey step. Typical uses: * **Add leads or cases to a base:** append a record with fields from the traveler, for a team that manages work in Airtable. * **Build an ops log:** add one record per step reached, for tracking outside a database. * **Hand off to another team:** write to a shared base so a non-technical team can follow up. ## Configure the point 1. Drag **Airtable: create record** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Personal access token**, enter an Airtable personal access token with permission to write records to the target base. It’s stored masked, like a webhook secret header. ![Airtable create record drawer with Step Name filled and Personal access token masked](https://docs.pushwoosh.com/journey-elements-airtable-create-record-1.webp) 4. In **Base id**, enter the target Airtable base id. ![Base id field with an Airtable base id](https://docs.pushwoosh.com/journey-elements-airtable-create-record-2.webp) 5. In **Table id or name**, enter the target table, by id or by name. ![Table id or name field set to Customers](https://docs.pushwoosh.com/journey-elements-airtable-create-record-3.webp) 6. In **Fields**, add one row per Airtable field to write. Enter the field name and the value. Names must be non-empty and unique. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Fields name and value table with Email mapped to a tag macro](https://docs.pushwoosh.com/journey-elements-airtable-create-record-4.webp) Example With **Table id or name** set to `Customers`, setting **Fields** to `Email` with the traveler’s email (`{{tag:Email}}`) adds a record to that table’s `Email` field. 7. Click **Save**. Note If you configured this point with the older single name/value pair, opening it pre-fills those values into **Fields**. Save the point once to move it onto the current version. Until you do, it keeps running with the version it was configured against. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | -------------------- | ------------------- | | `airtable_record_id` | The new record’s ID | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to look up this specific record later, for example through Airtable’s own API. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Logging a new support case in Airtable A team tracks support cases in Airtable and wants a row created the moment a case opens, without anyone entering it by hand. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `SupportCaseOpened` event. 2. Add **Airtable: create record** right after it. Set **Table id or name** to `Cases`. In **Fields**, set `Email` to `{{tag:Email}}`. 3. Click **Save**. Once this runs, every new support case gets a row in the Cases table with the traveler’s email already filled in. --- # Amplitude: set user properties **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/amplitude-set-user-properties/ Update the Amplitude user profile of a customer journey traveler, so Amplitude segmentation and cohorts match Pushwoosh tags. --- Use **Amplitude: set user properties** to update the Amplitude user profile of the traveler from a journey step, through the [HTTP API V2](https://amplitude.com/docs/apis/analytics/http-v2)’s `$identify` operation, so segmentation and cohorts on the Amplitude side see the same data as Pushwoosh. Pushwoosh sets Amplitude’s `insert_id` for you from the traveler and this step. That means the same traveler passing through this exact point twice, for example on a second lap of a looping journey, updates the same Amplitude profile instead of writing a duplicate identify event. Typical uses: * **Sync tags into Amplitude segments:** push plan tier, trial status, or another tag onto the Amplitude profile so Amplitude cohorts match Pushwoosh audiences. * **Keep profiles current without a separate sync job:** update the profile at the exact journey step where the tag changed. * **Avoid duplicate identify calls on a loop:** the built-in deduplication means a repeated pass through this point updates the same profile instead of writing twice. ## Configure the point 1. Drag **Amplitude: set user properties** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **API key**, enter the project API key from Amplitude: **Settings > Projects > your project > General**. It’s stored masked, like a webhook secret header. Pushwoosh never displays or returns it once saved, and it never appears in the point’s stored request or in the [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log). ![Amplitude set user properties drawer with Step Name filled and API key masked](https://docs.pushwoosh.com/journey-elements-amplitude-set-user-properties-1.webp) 4. In **Amplitude endpoint**, pick the region the Amplitude project was created in: `api2.amplitude.com` for a US project, `api.eu.amplitude.com` for an EU one. A key from one region is rejected by the other. ![Amplitude endpoint field set to api2.amplitude.com](https://docs.pushwoosh.com/journey-elements-amplitude-set-user-properties-2.webp) 5. In **User properties**, add one row per property to set on the Amplitude profile. Enter the property name and the value. Names must be non-empty and unique. A value that’s a single traveler macro keeps its own type in Amplitude (`{{tag:Age}}` arrives as a number, `{{tag:Trial Ends}}` as a date). Any other text is sent as a string. Macros use the same syntax as a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![User properties table with user_id and plan mapped to device and tag macros](https://docs.pushwoosh.com/journey-elements-amplitude-set-user-properties-3.webp) Example Setting **User properties** to `user_id` with the traveler’s ID (`{{device:user_id}}`) and `plan` with their plan tier (`{{tag:Plan}}`) sets those two properties on their Amplitude profile. 6. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | --------------------------- | --------------------------------------- | | `amplitude_events_ingested` | The number of events Amplitude ingested | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to confirm the update was ingested or branch on it with [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/). ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Keeping a subscriber’s Amplitude profile in sync A team wants a traveler’s plan tier to update on their Amplitude profile as soon as it changes in Pushwoosh. 1. Add **Amplitude: set user properties** right after the step where the plan tier changes. In **User properties**, set `plan` to `{{tag:Plan}}`. 2. Click **Save**. Once this runs, the Amplitude profile reflects the plan change without a separate sync job. --- # Amplitude: track event **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/amplitude-track-event/ Send one Amplitude event per traveler from a customer journey step, deduplicated so a looping journey doesn't double-count. --- Use **Amplitude: track event** to send one event to an Amplitude project from a journey step, through the [HTTP API V2](https://amplitude.com/docs/apis/analytics/http-v2), with the properties you list. Pushwoosh sets Amplitude’s `insert_id` for you from the traveler and this step. That means the same traveler passing through this exact point twice, for example on a second lap of a looping journey, lands as one deduplicated event in Amplitude, not two. Typical uses: * **Log journey milestones:** send an event when a traveler reaches a step, so it shows up next to your product events in Amplitude funnels. * **Feed a cross-channel funnel:** log push, email, or SMS milestones alongside in-app events for one funnel view in Amplitude. * **Track a looping journey safely:** the built-in deduplication means a traveler who loops back through this step still lands as one event, not several. ## Configure the point 1. Drag **Amplitude: track event** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **API key**, enter the project API key from Amplitude: **Settings > Projects > your project > General**. It’s stored masked, like a webhook secret header. Pushwoosh never displays or returns it once saved, and it never appears in the point’s stored request or in the [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log). ![Amplitude track event drawer with Step Name filled and API key masked](https://docs.pushwoosh.com/journey-elements-amplitude-track-event-1.webp) 4. In **Amplitude endpoint**, pick the region the Amplitude project was created in: `api2.amplitude.com` for a US project, `api.eu.amplitude.com` for an EU one. A key from one region is rejected by the other. ![Amplitude endpoint field set to api2.amplitude.com](https://docs.pushwoosh.com/journey-elements-amplitude-track-event-2.webp) 5. In **Event name**, enter the event as it should appear in Amplitude, for example `journey_welcome_push_sent`. It takes the same macros as **Event properties**. ![Event name field set to journey_welcome_push_sent](https://docs.pushwoosh.com/journey-elements-amplitude-track-event-3.webp) 6. In **Event properties**, add one row per property to send with the event. Enter the property name and the value. Names must be non-empty and unique. A value that’s a single traveler macro keeps its own type in Amplitude (`{{tag:Age}}` arrives as a number, `{{tag:Trial Ends}}` as a date). Any other text is sent as a string. Macros use the same syntax as a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Event properties table with plan mapped to a tag macro](https://docs.pushwoosh.com/journey-elements-amplitude-track-event-4.webp) Example Setting **Event properties** to `plan` with the traveler’s plan tier (`{{tag:Plan}}`) sends that property with the event to Amplitude. 7. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | --------------------------- | --------------------------------------- | | `amplitude_events_ingested` | The number of events Amplitude ingested | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to confirm the event was ingested or branch on it with [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/). ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Logging journey sends into an Amplitude funnel A team wants a welcome push send to show up in the same Amplitude funnel as their other product events. 1. Add **Amplitude: track event** at the point in the journey where the event should fire. Set **Event name** to `journey_welcome_push_sent`. 2. Click **Save**. Once this runs, the send shows up in Amplitude next to the rest of the product events, so the funnel reflects the full picture instead of a gap where the journey step doesn’t report. --- # AppsFlyer: send event **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/appsflyer-send-event/ Send one server-to-server in-app event per traveler to AppsFlyer, carrying the install attribution, from a customer journey step. --- The **AppsFlyer: send event** Point template sends a [server-to-server in-app event](https://dev.appsflyer.com/hc/reference/events_android) to AppsFlyer for every traveler that passes this step. The event carries the attribution of the install, so the journey step shows up in AppsFlyer reports and can be handed to ad networks as a conversion. Developer assistance needed The point doesn’t set the AppsFlyer ID tag by itself. Without app code that reads `AppsFlyerLib.getAppsFlyerUID()` and saves it as a Pushwoosh tag after the AppsFlyer SDK starts, the tag stays empty on every device and the point [skips every traveler](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/appsflyer-send-event/#when-no-request-is-sent). Share this with your development team: * Java ```java String appsflyerId = AppsFlyerLib.getInstance().getAppsFlyerUID(context); Pushwoosh.getInstance().setTags(new TagsBundle.Builder().putString("AppsFlyer ID", appsflyerId).build()); ``` * Swift ```swift let appsflyerId = AppsFlyerLib.shared().getAppsFlyerUID Pushwoosh.sharedInstance().setTags(["AppsFlyer ID": appsflyerId]) ``` ## Configure the point 1. Drag **AppsFlyer: send event** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **App ID**, enter the app identifier from AppsFlyer: **App Settings**, `id123456789` for iOS or `com.example.app` for Android. ![AppsFlyer send event drawer with Step Name and App ID fields](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-1.webp) 4. In **Dev key**, enter the app’s dev key from **App Settings > Dev Key**. It’s stored masked, like a webhook secret header — Pushwoosh never displays or returns it once saved, and it never appears in the point’s stored request or in the [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log). ![Dev key field in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-2.webp) 5. In **Event name**, enter the name the event should get in AppsFlyer, a standard one (`af_purchase`, `af_complete_registration`) or your own (`paywall_reached`). ![Event name field in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-3.webp) 6. In **Tag with the AppsFlyer ID**, type the name of the Pushwoosh tag where the app saves the AppsFlyer ID after the AppsFlyer SDK starts (see the developer note above). Enter the tag name as plain text. Point templates don’t have the tag picker that Push or Email use. AppsFlyer matches a server event to an install only by this ID, so get the tag name exactly right. ![Tag with the AppsFlyer ID field in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-4.webp) 7. Optional. In **Customer user ID**, keep the default `{{device:user_id}}` to send the Pushwoosh user ID, or enter your own identifier to reconcile in AppsFlyer. ![Customer user ID field in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-5.webp) 8. Optional. In **Currency**, keep the default `USD` or enter another ISO 4217 code. It’s sent as `eventCurrency`. ![Currency field in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-6.webp) 9. Optional. In **Event value**, add the parameters to send as AppsFlyer’s `eventValue` payload — see [key/value fields](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#configuring-list-and-keyvalue-fields). The table starts pre-filled with an `af_revenue` and an `af_currency` row; clear either one you don’t need. Use `af_revenue` for the amount, and `af_content_id` or `af_content_type` for your own. There’s no separate numeric revenue field: `af_revenue` is a string cell in this table, the same as any other parameter, and it takes a macro like `{{tag:Price}}`. A blank cell isn’t sent. ![Event value field with af_revenue and af_currency rows in the AppsFlyer send event drawer](https://docs.pushwoosh.com/journey-elements-appsflyer-send-event-7.webp) 10. Click **Save**. ## When no request is sent Pushwoosh skips the request (and the traveler continues to the next step) when the tag in **Tag with the AppsFlyer ID** is empty for that traveler. That isn’t a failed request. AppsFlyer would have nothing to match the event to. A failed request (one that did have an AppsFlyer ID) drops the traveler from the journey instead. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Reporting a completed purchase to AppsFlyer An app wants a purchase made later in a journey to carry the install’s attribution in AppsFlyer, so it can be handed to ad networks as a conversion. This assumes the development team already saved the AppsFlyer ID into a Pushwoosh tag, as described above. 1. Add **AppsFlyer: send event** right after the step that confirms the purchase. Set **Event name** to `af_purchase`. In **Event value**, set `af_revenue` to `{{tag:LastPurchaseAmount}}`. 2. Click **Save**. Once this runs, the purchase shows up in AppsFlyer’s reports with the install’s attribution, ready to hand to ad networks as a conversion. --- # BigQuery: insert row **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/bigquery-insert-row/ Stream a row into a Google BigQuery table from a customer journey step, using the connected Google account. --- Use **BigQuery: insert row** to stream one row into a BigQuery table from a journey step, through the streaming insert API. Like Google Sheets: add row, this point authenticates through the Google account connected once for the whole account. There’s no token field on the point itself. Typical uses: * **Feed a reporting table:** insert a row per journey step reached, for a dashboard or scheduled query downstream. * **Log events as they happen:** stream raw journey data into BigQuery instead of waiting on a batch export. * **Join with existing data:** insert a device or tag value your BigQuery tables can join against your own datasets. ## Before you start Connect a Google account under **Settings > 3rd-party integrations > Google**. This is the same connection [Google Sheets: add row](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/google-sheets-add-row/) uses. After you connect Google, [install the integration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#install-an-integration) so the point appears in the journey builder. This is not the **Google BigQuery** card on that page. That one is the [BigQuery integration](https://docs.pushwoosh.com/product/integrations/google-bigquery-integration/): it streams every message event of an application into a fixed schema on a service account key. This point writes one row you define, at the step you place it on, and takes no key of its own. ![Settings 3rd-party integrations showing the Google connect card next to the separate Google BigQuery card](https://docs.pushwoosh.com/journey-elements-bigquery-insert-row-1.webp) Caution If you connected Google before BigQuery support was added, reconnect it once. A connection keeps the permissions it was granted with, so an older one can write to Sheets but not to BigQuery, and the point fails with a `403`. Two things also need to be in place on the Google Cloud side, or the request fails: * The dataset and the table have to exist already. This point writes rows. It never creates a table. * The connected Google account needs the **BigQuery Data Editor** role (or any role granting `bigquery.tables.updateData`) on that dataset. ## Configure the point 1. Drag **BigQuery: insert row** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Project ID**, enter the Google Cloud project ID. Use the id, not the display name, for example `my-analytics-prod`. ![BigQuery insert row drawer with Step Name filled and Project ID set to my-analytics-prod](https://docs.pushwoosh.com/journey-elements-bigquery-insert-row-2.webp) 4. In **Dataset**, enter the dataset name. ![Dataset field set to marketing](https://docs.pushwoosh.com/journey-elements-bigquery-insert-row-4.webp) 5. In **Table**, enter the table name. ![Table field set to journey_events](https://docs.pushwoosh.com/journey-elements-bigquery-insert-row-5.webp) 6. In **Columns**, add one row per BigQuery column to write. Enter the column name and the value. Names must be non-empty and unique. Every column you list has to exist in the table’s schema. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Columns name and value table with device_id mapped to a device hwid macro](https://docs.pushwoosh.com/journey-elements-bigquery-insert-row-6.webp) Example With **Dataset** set to `marketing` and **Table** to `journey_events`, setting **Columns** to `device_id` with the traveler’s device ID (`{{device:hwid}}`) writes a row into that table’s `device_id` column. 7. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs This point doesn’t return an attribute on success. If BigQuery rejects a row, the error is written to `bigquery_insert_error` instead. See [Failures and unexpected responses](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/bigquery-insert-row/#failures-and-unexpected-responses). ### Failures and unexpected responses Open [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) in the point’s drawer and check the response: * **401** — the Google connection is missing or expired. Reconnect it under **Settings > 3rd-party integrations**. * **403 with `accessDenied: Streaming insert is not allowed in the free tier`** — the project has no billing account attached. Streaming inserts, which is what this point uses, are not part of the BigQuery sandbox. Enable billing on the project and the same request goes through. * **403 with `accessDenied` or `insufficientPermissions`** — the connection was granted before BigQuery support was added (reconnect it), or the Google account has no write access to the dataset. * **403 with `quotaExceeded` or `rateLimitExceeded`** — the project’s streaming insert quota is spent. Nothing to reconnect. The journey has to send less, or the quota has to be raised in Google Cloud. * **404** — the project, dataset, or table name doesn’t match anything. Caution A row BigQuery rejects (an unknown column, or a value that doesn’t fit the column’s type) comes back as **200 with an `insertErrors` body**, not as an error status. The traveler moves on as if the write succeeded. The first such message is written into the `bigquery_insert_error` traveler attribute. Condition split can’t branch on it directly, so save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) first, then branch on that Tag with a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/). The full body is in the Calls log. A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Feeding a BigQuery reporting table A team wants journey activity in the same BigQuery dataset their scheduled reports already query. 1. Add **BigQuery: insert row** right after the step you want to log. Set **Dataset** to `marketing` and **Table** to `journey_events`. In **Columns**, set `device_id` to `{{device:hwid}}`. 2. Click **Save**. Once this runs, the row lands in the same table the scheduled query reads from, so the next report run already includes it. --- # Dynamics 365: create or update record **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/dynamics-upsert-record/ Create or update a Dataverse row from a customer journey step, matched by an alternate key, using a connected Entra ID app. --- Use **Dynamics 365: create or update record** to upsert one Dataverse row from a journey step, matched on an alternate key column: it updates the row when the value is already there and creates it when it isn’t. Dataverse has no static API key, so this point authenticates through an Entra ID application registered once for the whole account. There’s no token field on the point itself. Typical uses: * **Keep Dynamics 365 in sync:** create or update a row as the traveler moves through the journey, so your CRM sees current data. * **Sync journey activity into CRM:** set a column when a traveler reaches a milestone, so your team can filter or report on it. * **Avoid duplicate rows:** matching by alternate key means a traveler who passes through this point again updates the same row instead of creating a new one. ## Before you start Register an application in Entra ID and connect it under **Settings > 3rd-party integrations > Microsoft Dynamics 365**: 1. In the [Microsoft Entra admin center](https://entra.microsoft.com), register an application and create a client secret for it. 2. In the **Power Platform admin center**, open your target environment, add the application as an **Application user**, and assign it a security role that can create and update rows on the table you plan to write to. 3. In Pushwoosh, in **Environment URL**, enter the environment’s URL with `https://` and no trailing path, for example `https://acme.crm4.dynamics.com`. 4. In **Directory (tenant) ID** and **Application (client) ID**, enter the application’s IDs from its Entra ID overview page. 5. In **Client secret**, paste the secret’s value. It’s stored write-only: Pushwoosh never shows it again, so paste a new one to replace it. 6. Click **Connect Dynamics 365**. ![Settings Microsoft Dynamics 365 connect form with Environment URL, tenant ID, client ID, and secret](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-1.webp) After you connect Dynamics 365, [install the integration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#install-an-integration) so the point appears in the journey builder. Note The alternate key you plan to match on has to already exist on the table in Dataverse. Set it up in the Power Platform admin center (or in your solution) first — the point can’t create one for you. ## Configure the point 1. Drag **Dynamics 365: create or update record** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Environment host**, enter the same environment’s host without `https://`, for example `acme.crm4.dynamics.com`. ![Dynamics 365 create or update record drawer with Step Name filled and Environment host set to a Dynamics domain](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-2.webp) 4. In **Table**, enter the entity set name, plural — `contacts`, `leads`, or a custom table’s set name. ![Table field set to contacts](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-3.webp) 5. In **Alternate key column**, enter the column covered by an alternate key on that table, for example `pw_hwid`. ![Alternate key column field set to pw_hwid](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-4.webp) 6. In **Alternate key value**, enter the value to match on, usually a macro like `{{device:hwid}}`. Avoid values with quotes, since the value goes into the request URL. ![Alternate key value field set to a device hwid macro](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-5.webp) 7. In **Columns**, add one row per Dataverse column to write. Enter the column name and the value. Names must be non-empty and unique. A value can be fixed text or a traveler macro such as `{{device:hwid}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Columns name and value table with emailaddress1 mapped to a tag macro](https://docs.pushwoosh.com/journey-elements-dynamics-upsert-record-6.webp) Example With **Table** set to `contacts`, **Alternate key column** to `pw_hwid`, and **Alternate key value** to the traveler’s device ID (`{{device:hwid}}`), the point matches an existing row or creates a new one. Setting **Columns** to `emailaddress1` with the traveler’s email (`{{tag:Email}}`) fills in that column. 8. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs Unlike [Salesforce: create or update record](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/salesforce-upsert-record/), this point doesn’t return the row’s ID into a traveler attribute. If a later step needs it, look the row up again by the same alternate key value. ### Failures and unexpected responses Open [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) in the point’s drawer and check the response: * **401** — the connection’s client secret was rejected or has expired; reconnect under **Settings > 3rd-party integrations**. * **403** — the application user’s security role doesn’t grant create or update access on that table. * **404** — **Environment host** or **Table** doesn’t match anything in that environment. * **400** — **Alternate key column** isn’t defined as an alternate key on that table, or a name in **Columns** isn’t a real column. A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Syncing an enterprise pricing request into Dynamics 365 A B2B SaaS app lets a user tap **Request enterprise pricing** in-app. The team wants that request upserted into Dynamics 365, matched so a repeat tap updates the same row instead of creating a duplicate. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on an `EnterprisePricingRequested` event. 2. Add **Dynamics 365: create or update record** right after it. Set **Table** to `contacts`, **Alternate key column** to `pw_hwid`, and **Alternate key value** to `{{device:hwid}}`. In **Columns**, set `emailaddress1` to `{{tag:Email}}`. 3. Click **Save**. Once this runs, the request exists as a Dynamics 365 contact matched by device, ready for the sales team to work from CRM instead of the app’s own data. --- # Microsoft Fabric: ingest row **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/fabric-eventhouse-ingest/ Stream a row into a Microsoft Fabric Eventhouse table from a customer journey step, using a connected Entra ID app. --- Use **Microsoft Fabric: ingest row** to stream one row into an Eventhouse (KQL) table from a journey step, through the Fabric ingest API. Unlike the other Integrations points that take their own token, this one authenticates through an Entra ID application connected once for the whole account. There’s no token field on the point itself. Typical uses: * **Feed a reporting table:** insert a row per journey step reached, for a dashboard or scheduled query downstream. * **Log events as they happen:** stream raw journey data into the Eventhouse instead of waiting on a batch pipeline. * **Join with existing data:** insert a device or tag value your Fabric tables can join against your own datasets. ## Before you start Connect an Entra ID application under **Settings > 3rd-party integrations > Microsoft Azure**: 1. Register an application in your Entra ID directory and create a client secret for it. 2. In **Directory (tenant) ID**, enter your tenant, for example `contoso.onmicrosoft.com`. 3. In **Application (client) ID**, enter the app registration’s client ID. 4. In **Client secret**, paste the secret. It’s stored write-only: Pushwoosh never shows it again, so paste a new one to replace it. 5. Click **Connect Azure**. After you connect Azure, [install the integration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#install-an-integration) so the point appears in the journey builder. ![Settings Microsoft Azure connect form with tenant ID, client ID, and client secret fields](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-1.webp) Caution A client secret has its own expiration date, set when you created it in Entra ID. On the day it expires, the point starts failing and the Control Panel just shows a generic “reconnect the integration” state — nothing points at the secret specifically. Put a reminder on the secret’s expiration date so you rotate it before that happens, not after. Three things also need to be in place on the Eventhouse side, or the request fails: * The target table needs a JSON ingestion mapping — streaming ingestion of JSON doesn’t work without one. * The app registration needs the `ingestor` role on the Eventhouse. There’s no button for this in the Fabric UI — grant it by running this in the Eventhouse’s query window: ```plaintext .add database [''] ingestors ('aadapp=;') ``` * The KQL database needs its streaming ingestion policy enabled. Run this in the same query window: ```plaintext .alter database [''] policy streamingingestion enable ``` Keep the `['...']` around the database name in these commands if it contains a hyphen — a bare hyphenated name fails to parse. ## Configure the point 1. Drag **Microsoft Fabric: ingest row** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Query URI host**, enter your Eventhouse’s host from its **Query URI**, on the Eventhouse’s **System overview** page. Don’t include `https://`, for example `trd-abc123.z9.kusto.fabric.microsoft.com`. ![Microsoft Fabric ingest row drawer with Step Name filled and Query URI host set to a Kusto URL](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-2.webp) 4. In **KQL database**, enter the target database’s name. ![KQL database field set to am-test](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-4.webp) 5. In **Table**, enter the target table’s name. ![Table field set to Events](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-5.webp) 6. In **Ingestion mapping**, enter the name of the JSON mapping created on that table. ![Ingestion mapping field set to EventsJsonMapping](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-6.webp) 7. In **Columns**, add one row per Eventhouse column to write. Enter the column name and the value. Names must be non-empty and unique. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Columns name and value table with device_id mapped to a device hwid macro](https://docs.pushwoosh.com/journey-elements-fabric-eventhouse-ingest-7.webp) Example With **Table** set to `Events`, setting **Columns** to `device_id` with the traveler’s device ID (`{{device:hwid}}`) writes a row into that table’s `device_id` column. 8. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs This point doesn’t map any response data back into the journey. ### Failures and unexpected responses Open [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) in the point’s drawer and check the response: * **401 or 403** — the connected Entra ID application’s credentials or its `ingestor` role are wrong or missing. * **520 with `StreamingIngestionException: Failed to initialize cursor tracker...`** — the KQL database’s streaming ingestion policy isn’t enabled. The error’s own `permanent: false` flag reads as “retry later,” but retrying doesn’t fix this. Run `.alter database [''] policy streamingingestion enable` in the Eventhouse’s query window (see [Before you start](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/fabric-eventhouse-ingest/#before-you-start)). A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Streaming journey activity into a Fabric dashboard A team wants a live dashboard of every step reached across an active journey, without waiting on a batch export. 1. Add **Microsoft Fabric: ingest row** right after the step you want to track. Set **KQL database** to `am-test`, **Table** to `Events`, and **Ingestion mapping** to `EventsJsonMapping`. In **Columns**, set `device_id` to `{{device:hwid}}`. 2. Click **Save**. Once this runs, each traveler who reaches this step shows up in the Eventhouse table within seconds, ready for the dashboard. --- # Gemini: generate text **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/gemini-generate-text/ Ask a Google Gemini model to generate a short piece of text in a customer journey, and reuse it in a later push, email, or SMS. --- Use **Gemini: generate text** to ask a Google Gemini model for a short piece of text mid-journey, then reuse it in a later push, email, or SMS. This point authenticates with its own Gemini API key. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Personalize a message line:** draft a one-line hook from a traveler’s tags, then drop it into the next push or email with [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Summarize before sending:** turn a few tags, such as last order or browsing category, into a short natural-language line a template can display. * **Vary the wording:** ask for a slightly different phrasing on each run so travelers don’t see identical text every time they pass through. ## Configure the point 1. Drag **Gemini: generate text** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Gemini API key**, enter your Gemini API key. It’s stored masked, like a webhook secret header. ![Gemini generate text drawer with Step Name filled and Gemini API key masked](https://docs.pushwoosh.com/journey-elements-gemini-generate-text-1.webp) 4. In **Model**, choose the Gemini model to use. Defaults to **gemini-2.5-flash-lite**. * **gemini-2.5-flash-lite:** the default. * **gemini-2.5-flash:** costs more per token than Flash Lite. **Gemini 2.5 Pro** isn’t offered here: it can’t turn off its own reasoning step, so it isn’t guaranteed to finish within the point’s 10-second timeout. Every call bills your own Gemini key for every traveler who reaches this step. ![Model field set to gemini-2.5-flash-lite with the Pro exclusion note](https://docs.pushwoosh.com/journey-elements-gemini-generate-text-2.webp) 5. In **Prompt**, write what you want the model to generate. It takes the same macros as any Webhook body, for example `{{device:hwid}}`. ![Prompt field with a product recommendation prompt using a tag macro](https://docs.pushwoosh.com/journey-elements-gemini-generate-text-3.webp) 6. Click **Save**. Reasoning is turned off and responses are capped at about 300 tokens (roughly a short paragraph) to keep the request inside the [Webhook timeout](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests). Ask for something that short. A longer or more detailed prompt doesn’t get more room to answer. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ------------------- | ------------------------------------ | | `llm_text` | The generated text | | `llm_input_tokens` | Token count for the request’s input | | `llm_output_tokens` | Token count for the request’s output | ### How to use `llm_text` in later steps * **Drop it into a push, email, or SMS:** use [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Branch on it with [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/):** save it to a Tag first with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), then split on that Tag. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). A response that comes back but isn’t what you expected keeps the traveler in the journey: * **Text cut off at the 300-token cap:** `llm_text` holds the truncated text. * **Model declined to answer:** `llm_text` holds whatever it replied with instead. ## Example scenario: Drafting an order follow-up tip in SMS with Gemini A team wants an order-confirmation SMS to include a short, product-specific care tip instead of just the order number. 1. Add **Gemini: generate text** right after the step that confirms the order. In **Prompt**, write `Write one short care tip for someone who just bought {{tag:Last Purchased Category}}.` 2. Add a **Send SMS** step right after it, using [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) to insert `llm_text` into the SMS text. Once this runs, each order confirmation includes a tip relevant to what the traveler bought, instead of the same line for every order. --- # Google Sheets: add row **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/google-sheets-add-row/ Append a row of dynamic values to a Google spreadsheet from a customer journey, using a connected Google account. --- Use **Google Sheets: add row** to append a row to a Google spreadsheet when a traveler reaches this step. Map traveler values into the cells so the sheet updates as the journey runs, without building a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) request yourself. Typical uses: * **Keep a simple log:** each time someone reaches a step, add a new row with their ID and a few tags. The team opens the sheet to see who went through. * **Pass leads to another team:** write Name, Email, and other tags into a shared sheet so sales or support can follow up. * **Track completions without a database:** build a live list of who finished a branch or campaign when a spreadsheet is enough. ## Before you start Connect a Google account under **Settings > 3rd-party integrations > Google**. After you connect Google, [install the integration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#install-an-integration) so the point appears in the journey builder. ## Configure the point 1. Drag **Google Sheets: add row** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Spreadsheet ID**, enter the ID from the sheet’s URL: `docs.google.com/spreadsheets/d//edit`. ![Google Sheets add row drawer with Step Name filled and Spreadsheet ID taken from the sheet URL](https://docs.pushwoosh.com/journey-elements-google-sheets-add-row-3.webp) 4. In **Range**, enter where to append the row, for example `Sheet1!A1`. Don’t include spaces. Pushwoosh uses the value in the request as-is. Defaults to `Sheet1!A1`. ![Range field set to Sheet1!A1 for where the new row is appended](https://docs.pushwoosh.com/journey-elements-google-sheets-add-row-4.webp) 5. In **Columns**, enter what to write in each cell of the new spreadsheet row, left to right. The first value goes to column A, the second to B, and so on. Click **+ Add value** for each next cell. Use fixed text or a traveler macro such as `{{device:hwid}}`, `{{tag:Name}}`, or `{{tag:Email}}` (same macros as a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body). Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. Keep at least one non-empty value. ![Google Sheets add row Columns list with several value rows ordered left to right for the spreadsheet](https://docs.pushwoosh.com/journey-elements-integrations-2.webp) Example Filling **Columns** with the traveler’s device ID, name, and email (`{{device:hwid}}`, `{{tag:Name}}`, `{{tag:Email}}`) appends a row like `a1b2c3d4e5f6 | Jamie Lee | jamie.lee@example.com` to the sheet, one value per column. 6. Click **Save**. Note If you configured this point with the older fixed **Column 1** / **Column 2** / **Column 3** fields, opening it pre-fills those values into **Columns**. Save the point once to move it onto the current version. Until you do, it keeps running with the version it was configured against. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs This point doesn’t map any response data back into the journey. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Logging journey activity for an ops spreadsheet A team wants a lightweight log of every high-value order a journey processes, without building a reporting pipeline. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `HighValueOrderPlaced` event. 2. Add **Google Sheets: add row** right after it. Set **Spreadsheet ID** to the ops sheet’s ID and **Range** to `Sheet1!A1`. In **Columns**, add the traveler’s device ID, name, and order value: `{{device:hwid}}`, `{{tag:Name}}`, `{{tag:Order Value}}`. 3. Click **Save**. Once this runs, every high-value order appends a row to the sheet as it happens, ready for the team to scan without opening Pushwoosh. --- # HubSpot: add timeline event **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-add-timeline-event/ Write a line on a HubSpot contact timeline saying the traveler passed this step, using the connected HubSpot integration. --- Use **HubSpot: add timeline event** to log that a traveler passed this journey step directly onto a HubSpot contact’s timeline, alongside emails, calls, and other CRM activity. The event carries the journey’s name, the step’s name, and whether the traveler is subscribed to push. Put this point after one that already knows the contact, for example [HubSpot: get contact properties](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-get-contact-properties/), [HubSpot: create or update contact](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-create-or-update-contact/), or [HubSpot: update subscription](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-update-subscription/) — all three put the contact’s ID into attribute `hubspot_contact_id`, which this point needs. Unlike the other HubSpot points, this one has no **Private app token** field: it authenticates through the account’s connected HubSpot integration instead of a token you paste into the point. ## Before you start Connect Pushwoosh to HubSpot first — see [HubSpot integration](https://docs.pushwoosh.com/product/integrations/hubspot-integration/). Caution A portal connected before Pushwoosh started requesting the `timeline` scope doesn’t have it, and this point fails with a permission error. The traveler drops from the journey like any other failed request. Reconnect the integration to grant the scope, following the note on reconnecting in [HubSpot integration](https://docs.pushwoosh.com/product/integrations/hubspot-integration/#add-pushwoosh-integration-to-hubspot). ## Configure the point 1. Drag **HubSpot: add timeline event** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **HubSpot contact ID**, click **Insert Dynamic Value** to pick `hubspot_contact_id` from the HubSpot point earlier in this journey that found or wrote the contact. 4. If Pushwoosh is connected to more than one HubSpot portal, enter the numeric ID of the one to write to in **HubSpot portal ID**. Leave it empty when there’s just one. 5. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. This point doesn’t return an attribute. Unlike [HubSpot: get contact properties](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-get-contact-properties/) and [HubSpot: update subscription](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-update-subscription/), this point has a single path. There’s no **Found**/**Not found** branch to connect. ### Failures and unexpected responses Open [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) in the point’s drawer and check the response: * A permission error — the connected portal doesn’t have the `timeline` scope. See [Before you start](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-add-timeline-event/#before-you-start). * A request naming several portals — **HubSpot portal ID** is empty, and Pushwoosh is connected to more than one HubSpot portal. Fill it in with one of the IDs the response names. A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). --- # HubSpot: create or update contact **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-create-or-update-contact/ Create or update a HubSpot contact by email, with any number of properties, from a customer journey step. --- Use **HubSpot: create or update contact** to upsert one HubSpot contact by email from a journey step: it creates a new contact when the email isn’t found, or updates the listed properties when it is. This point authenticates with its own HubSpot private app token. It doesn’t use any account-level HubSpot integration configured elsewhere in Settings. Typical uses: * **Sync journey activity into CRM:** create or update a contact so sales sees journey progress without checking Pushwoosh. * **Enrich a contact:** set properties like plan or last purchase as the traveler’s tags update through the journey. * **Trigger a HubSpot workflow:** update a property a HubSpot workflow watches, from inside a Pushwoosh journey. ## Configure the point 1. Drag **HubSpot: create or update contact** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Private app token**, enter a HubSpot private app token with permission to write contacts. It’s stored masked, like a webhook secret header. 4. In **Contact email**, enter the email HubSpot uses to find an existing contact. It takes the same macros as **Properties**, for example `{{tag:Email}}`, so each traveler upserts their own contact instead of all of them colliding on one fixed address. Don’t repeat it in **Properties**. ![HubSpot Private app token masked and Contact email field set to a tag macro](https://docs.pushwoosh.com/journey-elements-hubspot-create-or-update-contact-2.webp) 5. In **Properties**, add one row per HubSpot property to set. Enter HubSpot’s internal property name (for example `firstname`, `phone`) and the value. Names must be non-empty and unique. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![HubSpot Properties name and value table with firstname and phone mapped to tag macros](https://docs.pushwoosh.com/journey-elements-integrations-3.webp) Example Setting **firstname** to the traveler’s name (`{{tag:Name}}`) and **phone** to their phone number (`{{tag:Phone}}`) fills in those two HubSpot properties on the upserted contact. 6. Click **Save**. Note If you configured this point with the older single name/value pair, opening it pre-fills those values into **Properties**. Save the point once to move it onto the current version. Until you do, it keeps running with the version it was configured against. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | -------------------- | ------------------------ | | `hubspot_contact_id` | The HubSpot contact’s ID | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to link back to this contact later, for example through HubSpot’s own API. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Syncing a new signup into HubSpot A team wants every new signup to exist as a HubSpot contact, without a separate import step. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `SignupCompleted` event. 2. Add **HubSpot: create or update contact** right after it. Set **Contact email** to `{{tag:Email}}`. In **Properties**, set `firstname` to `{{tag:Name}}`. 3. Click **Save**. Once this runs, every new signup exists as a HubSpot contact, created or updated by email so a repeat signup doesn’t create a duplicate. --- # HubSpot: get contact properties **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-get-contact-properties/ Read the named properties of one HubSpot contact into journey attributes, so a later step can print them or branch on them. --- The **HubSpot: get contact properties** Point template reads the properties you name off one HubSpot contact and hands them to the rest of the journey: a message can print them, a splitter can branch on them. This point authenticates with its own HubSpot private app token — it doesn’t use any account-level HubSpot integration configured elsewhere in Settings. ## Configure the point 1. Drag **HubSpot: get contact properties** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Private app token**, enter a HubSpot private app token with the `crm.objects.contacts.read` scope. It’s stored masked, like a webhook secret header. ![HubSpot get contact properties drawer with Step Name and Private app token fields](https://docs.pushwoosh.com/journey-elements-hubspot-get-contact-properties-1.webp) 4. In **Look the contact up by**, enter the internal name of the HubSpot property that holds the identifier — `email` by default, or a property of your own that carries the Pushwoosh User ID. HubSpot only looks a contact up by a property whose values it keeps unique. ![Look the contact up by field in the HubSpot get contact properties drawer](https://docs.pushwoosh.com/journey-elements-hubspot-get-contact-properties-2.webp) 5. In **Identifier value**, enter the value to look up, for example `{{device:user_id}}` or a tag of the traveler. ![Identifier value field in the HubSpot get contact properties drawer](https://docs.pushwoosh.com/journey-elements-hubspot-get-contact-properties-3.webp) 6. In **Properties to read**, add a row per HubSpot property’s internal name (for example `firstname`, `lifecyclestage`) — see [list fields](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#configuring-list-and-keyvalue-fields). Keep at least one non-empty value. ![Properties to read field in the HubSpot get contact properties drawer](https://docs.pushwoosh.com/journey-elements-hubspot-get-contact-properties-4.webp) 7. Click **Save**. ## Connect the branches After you save, the point shows two branches on the canvas. Connect a next step to each: * **Found:** the identifier matched an existing HubSpot contact. For example, continue with a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) or a message that uses the properties you read. * **Not found:** HubSpot answered with a 404 (no matching contact). The traveler is not dropped. The point does not retry or wait out a failed-request cooldown the way a real failed request does. For example, send a different path or a generic message. **Not found** is not a failed request. A real failed request drops the traveler instead. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Response On **Found**, later steps can use: * Each property you asked for as **`hubspot_`**. For example, `firstname` becomes `hubspot_firstname`. * The contact’s ID as **`hubspot_contact_id`**. Use them the same way as a [Webhook response attribute](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables). For example, branch with a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) or set a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response). On **Not found**, none of these attributes are available. ## Example scenario: Personalizing a message with a HubSpot contact’s plan A team wants a push to mention the traveler’s plan tier as stored in HubSpot, without keeping a separate copy of it in Pushwoosh tags. 1. Add **HubSpot: get contact properties** at the point in the journey where the plan should be read. Leave **Look the contact up by** at `email` and set **Identifier value** to `{{tag:Email}}`. In **Properties to read**, add `plan`. 2. Click **Save**, then connect **Found** to a **Push** step that uses `hubspot_plan` in its text, and connect **Not found** to a step that sends a generic push instead. Once this runs, a traveler with a matching HubSpot contact gets a push mentioning their actual plan, and a traveler with no match still gets a message instead of being dropped. --- # HubSpot: update subscription **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-update-subscription/ Write the push subscription state and journey step onto a HubSpot contact, so sales and email see what a person already gets. --- The **HubSpot: update subscription** Point template writes the push subscription state and the journey step onto an existing HubSpot contact, so sales and email marketing see what the person already gets from the app. This point authenticates with its own HubSpot private app token — it doesn’t use any account-level HubSpot integration configured elsewhere in Settings. ## Configure the point 1. Drag **HubSpot: update subscription** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Private app token**, enter a HubSpot private app token with the `crm.objects.contacts.write` scope. It’s stored masked, like a webhook secret header. ![HubSpot update subscription drawer with Step Name and Private app token fields](https://docs.pushwoosh.com/journey-elements-hubspot-update-subscription-1.webp) 4. In **Look the contact up by**, enter the internal name of the HubSpot property that holds the identifier — `email` by default, or a property of your own that carries the Pushwoosh User ID. HubSpot only looks a contact up by a property whose values it keeps unique. ![Look the contact up by field in the HubSpot update subscription drawer](https://docs.pushwoosh.com/journey-elements-hubspot-update-subscription-2.webp) 5. In **Identifier value**, enter the value to look up, for example `{{device:user_id}}` or a tag of the traveler. ![Identifier value field in the HubSpot update subscription drawer](https://docs.pushwoosh.com/journey-elements-hubspot-update-subscription-3.webp) 6. In **Properties to write**, add a row per HubSpot property’s internal name mapped to what to write into it. Names must be non-empty and unique. See [key/value fields](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#configuring-list-and-keyvalue-fields). Besides tags, the value can use `{{device:push_subscribed}}` for the push subscription state, `{{now}}` for the moment the traveler passed here, or `{{journey:name}}` / `{{point:name}}` for where. A cell holding a single macro keeps that macro’s own type. For example, `{{device:push_subscribed}}` alone arrives as a boolean, not the string `"true"`. ![Properties to write field in the HubSpot update subscription drawer](https://docs.pushwoosh.com/journey-elements-hubspot-update-subscription-4.webp) 7. Click **Save**. Note Each property in **Properties to write** has to already exist on the HubSpot contact object. This point doesn’t create new properties. Set them up in HubSpot first. If one doesn’t exist, HubSpot rejects the whole request. None of the properties in this step get written, and the traveler drops from the journey the same way as any other failed request. ## Connect the branches After you save, the point shows two branches on the canvas. Connect a next step to each: * **Updated:** the identifier matched an existing HubSpot contact and HubSpot returned the updated contact. For example, continue with the rest of the journey. * **Not found:** HubSpot answered with a 404 (no matching contact). The traveler is not dropped. The point does not retry or wait out a failed-request cooldown the way a real failed request does. For example, send a different path or create the contact first. **Not found** is not a failed request. A real failed request drops the traveler instead. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Response On **Updated**, later steps can use the HubSpot contact’s ID as **`hubspot_contact_id`**. Use it the same way as a [Webhook response attribute](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables). For example, set a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response). On **Not found**, that attribute is not available. ## Example scenario: Syncing push subscription status to HubSpot after opt-in A team wants sales and email marketing to see, right on the HubSpot contact, whether a person has opted into push. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `PushOptedIn` event. 2. Add **HubSpot: update subscription** right after it. Leave **Look the contact up by** at `email` and set **Identifier value** to `{{tag:Email}}`. In **Properties to write**, set `push_subscribed` to `{{device:push_subscribed}}`. 3. Click **Save**, then connect **Updated** to the rest of the journey and **Not found** to a step that creates the contact first, for example [HubSpot: create or update contact](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/hubspot-create-or-update-contact/). Once this runs, the HubSpot contact shows the traveler’s current push subscription state, so sales and email marketing don’t have to guess. --- # LLM: generate text **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/llm-generate-text/ Ask a Claude model to generate a short piece of text in a customer journey, and reuse it in a later push, email, or SMS. --- Use **LLM: generate text** to ask a Claude model for a short piece of text mid-journey, then reuse it in a later push, email, or SMS. This point authenticates with its own Anthropic API key. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Personalize a message line:** draft a one-line hook from a traveler’s tags, then drop it into the next push or email with [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Summarize before sending:** turn a few tags, such as last order or browsing category, into a short natural-language line a template can display. * **Vary the wording:** ask for a slightly different phrasing on each run so travelers don’t see identical text every time they pass through. ## Configure the point 1. Drag **LLM: generate text** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Anthropic API key**, enter your Anthropic API key. It’s stored masked, like a webhook secret header. ![LLM generate text drawer with Step Name filled and Anthropic API key masked](https://docs.pushwoosh.com/journey-elements-llm-generate-text-1.webp) 4. In **Model**, choose the Claude model to use. Defaults to **claude-haiku-4-5**. * **claude-haiku-4-5:** answers fastest. * **claude-sonnet-5:** a middle option between Haiku and Opus. * **claude-opus-5:** slower than the other two, but still finishes within the point’s 10-second timeout. Costs more per token than Sonnet or Haiku. Every call bills your own Anthropic key for every traveler who reaches this step. ![Model field set to claude-haiku-4-5 with the per-model timing note](https://docs.pushwoosh.com/journey-elements-llm-generate-text-2.webp) 5. In **Prompt**, write what you want the model to generate. It takes the same macros as any Webhook body, for example `{{device:hwid}}`. ![Prompt field with a product recommendation prompt using a tag macro](https://docs.pushwoosh.com/journey-elements-llm-generate-text-3.webp) 6. Click **Save**. Responses are capped at about 300 tokens (roughly a short paragraph) to keep the request inside the [Webhook timeout](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests). Ask for something that short. A longer or more detailed prompt doesn’t get more room to answer. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ------------------- | ------------------------------------ | | `llm_text` | The generated text | | `llm_input_tokens` | Token count for the request’s input | | `llm_output_tokens` | Token count for the request’s output | ### How to use `llm_text` in later steps * **Drop it into a push, email, or SMS:** use [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Branch on it with [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/):** save it to a Tag first with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), then split on that Tag. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). A response that comes back but isn’t what you expected keeps the traveler in the journey: * **Text cut off at the 300-token cap:** `llm_text` holds the truncated text. * **Model declined to answer:** `llm_text` holds whatever it replied with instead. ## Example scenario: Personalizing a re-engagement push with Claude A team wants a lapsed user’s re-engagement push to open with a one-line personalized hook instead of generic copy. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `UserLapsed` event. 2. Add **LLM: generate text** right after it. In **Prompt**, write `Write one short, upbeat sentence inviting {{tag:Name}} back, mentioning they browsed {{tag:Last Category}}.` 3. Add a **Push** step right after it. In the message editor, click [Personalization](https://docs.pushwoosh.com/product/personalization/dynamic-content/#how-to-personalize-a-push) and insert `llm_text` into the push text. Once this runs, each lapsed user gets a push that opens with a line Claude generated for them, instead of the same static copy for everyone. --- # Mattermost: post message **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mattermost-post-message/ Post a message to a Mattermost channel on your own instance from a customer journey step, using a bot token. --- Use **Mattermost: post message** to notify your team in Mattermost from a journey step, on your own Mattermost instance or Pushwoosh’s. This point authenticates with its own bot token. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Alert on high-value conversions:** post when a traveler completes a purchase or hits a goal, so the team sees it in real time. * **Flag a step that needs a human:** notify support or sales when a traveler reaches a point a bot can’t finish. * **Track journey health:** post a line each time a cohort finishes a branch, without building a dashboard. ## Configure the point 1. Drag **Mattermost: post message** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Instance host**, enter your Mattermost server’s host, for example `chat.example.com`. Don’t include `https://` or a trailing slash. Pushwoosh adds `https://` and the API path itself. ![Mattermost post message drawer with Step Name filled and Instance host set to chat.example.com](https://docs.pushwoosh.com/journey-elements-mattermost-post-message-1.webp) 4. In **Bot token**, enter the token of a bot account that’s a member of the target channel. It’s stored masked, like a webhook secret header, and Mattermost never returns it back to Pushwoosh. ![Bot token field masked](https://docs.pushwoosh.com/journey-elements-mattermost-post-message-2.webp) 5. In **Channel ID**, enter the channel’s 26-character ID, not its name. ![Channel ID field with a placeholder 26-character channel id](https://docs.pushwoosh.com/journey-elements-mattermost-post-message-3.webp) 6. In **Message**, enter the text to post. It takes the same macros as any Webhook body: `{{device:hwid}}`, `{{device:user_id}}`, `{{tag:Name}}`. ![Message field with an order alert using tag macros](https://docs.pushwoosh.com/journey-elements-mattermost-post-message-4.webp) 7. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | -------------------- | --------------------- | | `mattermost_post_id` | The created post’s ID | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to reference this specific post later, for example when troubleshooting delivery. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Alerting the success team about a churn-risk subscriber A subscription app wants its customer success team notified in Mattermost when a high-value subscriber’s plan is about to lapse, so someone can reach out before they churn. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `SubscriptionExpiringSoon` event. 2. Add **Mattermost: post message** right after it, posting to the success team’s channel with `{{tag:Name}} ({{tag:Plan}}) renews on {{tag:Renewal Date}}`. 3. Click **Save**. Once this runs, the success team gets a Mattermost post the moment a high-value subscriber is at risk, with enough detail to reach out. --- # Mixpanel: set profile **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mixpanel-set-profile/ Update the Mixpanel profile of a customer journey traveler, so segmentation and cohorts in Mixpanel match Pushwoosh tags. --- Use **Mixpanel: set profile** to update the Mixpanel profile of the traveler from a journey step, through the [Engage API](https://developer.mixpanel.com/reference/profile-set), authenticating with a project token. No separate connection or service account is needed. Typical uses: * **Sync tags into Mixpanel segments:** push plan, email, or another tag onto the Mixpanel profile so Mixpanel cohorts match Pushwoosh audiences. * **Keep profiles current without a separate sync job:** update the profile at the exact journey step where the tag changed. * **Pair with Track event:** use the same Distinct ID as the paired **Mixpanel: track event** point so events and profile updates land on the same Mixpanel person. ## Configure the point 1. Drag **Mixpanel: set profile** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **API host**, pick the region the Mixpanel project stores its data in: `api.mixpanel.com` for a US project, `api-eu.mixpanel.com` for an EU one. ![Mixpanel set profile drawer with Step Name filled and API host set to api.mixpanel.com](https://docs.pushwoosh.com/journey-elements-mixpanel-set-profile-1.webp) 4. In **Project token**, enter the project token from Mixpanel: **Settings > Project Settings**. The profile API takes the token in the request body, so, unlike **Mixpanel: track event**, no service account is required here. ![Project token field with a placeholder value](https://docs.pushwoosh.com/journey-elements-mixpanel-set-profile-2.webp) 5. In **Distinct ID**, enter whose profile to update. Use the same identifier as the paired **Mixpanel: track event** point, since both default to `{{device:user_id}}`. A mismatch sends events and profile updates to two different Mixpanel people. ![Distinct ID field set to a device user_id macro](https://docs.pushwoosh.com/journey-elements-mixpanel-set-profile-3.webp) 6. In **Update mode**, choose **$set** to overwrite the listed properties every time, or **$set_once** to write them only where the profile doesn’t already have a value. ![Update mode field set to $set](https://docs.pushwoosh.com/journey-elements-mixpanel-set-profile-4.webp) 7. In **Profile properties**, add one row per property to set on the Mixpanel profile. Enter the property name and the value. Names must be non-empty and unique. Use Mixpanel’s own reserved names where they apply, for example `$email` or `$name`. A value that’s a single traveler macro keeps its own type (`{{tag:Age}}` arrives as a number). Any other text is sent as a string. Macros use the same syntax as a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Profile properties table with $email and $name mapped to tag macros](https://docs.pushwoosh.com/journey-elements-mixpanel-set-profile-5.webp) Example Setting **Profile properties** to `$email` with the traveler’s email (`{{tag:Email}}`) and `$name` with their name (`{{tag:Name}}`) sets those two properties on their Mixpanel profile. 8. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs This point doesn’t map any response data back into the journey. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Keeping a Mixpanel profile in sync with a journey update A team wants a traveler’s plan tier to update on their Mixpanel profile as soon as it changes in Pushwoosh. 1. Add **Mixpanel: set profile** right after the step where the plan tier changes. Set **Distinct ID** to `{{device:user_id}}` (matching the paired **Mixpanel: track event** point) and **Update mode** to **$set**. In **Profile properties**, set `$email` to `{{tag:Email}}`. 2. Click **Save**. Once this runs, the Mixpanel profile reflects the plan change without a separate sync job. --- # Mixpanel: track event **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/mixpanel-track-event/ Send one Mixpanel event per traveler from a customer journey step, deduplicated so a looping journey doesn't double-count. --- Use **Mixpanel: track event** to send one event to a Mixpanel project from a journey step, through the [Import API](https://developer.mixpanel.com/reference/import-events), with the properties you list. This point authenticates with its own Mixpanel service account. It doesn’t use any account-level Mixpanel integration configured elsewhere in Settings. Pushwoosh sets Mixpanel’s `$insert_id` for you from the traveler. That means the same traveler passing through this exact point twice, for example on a second lap of a looping journey, lands as one deduplicated event in Mixpanel, not two. Deduplication is scoped to insert ID and event name together, so give every Track event point on the canvas its own **Event name**: reusing one across two different points can drop one traveler’s event as a duplicate of the other’s. Typical uses: * **Log journey milestones:** send an event when a traveler reaches a step, so it shows up next to your product events in Mixpanel funnels. * **Feed a cross-channel funnel:** log push, email, or SMS milestones alongside in-app events for one funnel view in Mixpanel. * **Track a looping journey safely:** the built-in deduplication means a traveler who loops back through this step still lands as one event, as long as each Track event point has its own Event name. ## Configure the point 1. Drag **Mixpanel: track event** from the **Integrations** section onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **API host**, pick the region the Mixpanel project stores its data in: `api.mixpanel.com` for a US project, `api-eu.mixpanel.com` for an EU one. ![Mixpanel track event drawer with Step Name filled and API host set to api.mixpanel.com](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-1.webp) 4. In **Project ID**, enter the numeric project ID from Mixpanel: **Settings > Project Settings**. ![Project ID field with a numeric placeholder](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-2.webp) 5. In **Service account username**, enter the username of a Mixpanel service account with the **Ingestion** role, from **Organization Settings > Service Accounts**. ![Service account username field with a placeholder value](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-3.webp) 6. In **Service account secret**, enter that service account’s secret. Pushwoosh combines the username and secret into the request’s Basic Authorization header for you. The secret is stored masked, like a webhook secret header, and Mixpanel only ever shows it once, when the service account is created. Copy it then. ![Service account secret field masked](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-4.webp) 7. In **Distinct ID**, enter who the event belongs to in Mixpanel. Defaults to `{{device:user_id}}`; use `{{device:hwid}}` instead to key events by device. ![Distinct ID field set to a device user_id macro](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-5.webp) 8. In **Event name**, enter the event name Mixpanel should show, for example `journey_welcome_push_sent`. ![Event name field set to journey_welcome_push_sent](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-6.webp) 9. In **Event properties**, add one row per property to send with the event. Enter the property name and the value. Names must be non-empty and unique. A value that’s a single traveler macro keeps its own type in Mixpanel (`{{tag:Age}}` arrives as a number, `{{tag:Trial Ends}}` as a date). Any other text is sent as a string. Macros use the same syntax as a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Event properties table with journey_name mapped to the journey:name macro](https://docs.pushwoosh.com/journey-elements-mixpanel-track-event-7.webp) Example Setting **Event properties** to `journey_name` with this journey’s name (`{{journey:name}}`) sends that property with the event to Mixpanel. 10. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs This point doesn’t map any response data back into the journey. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Logging journey sends into a Mixpanel funnel A team wants a welcome push send to show up in the same Mixpanel funnel as their other product events. 1. Add **Mixpanel: track event** right after the step that sends the welcome push. Set **Distinct ID** to `{{device:user_id}}` and **Event name** to `journey_welcome_push_sent`. In **Event properties**, set `journey_name` to `{{journey:name}}`. 2. Click **Save**. Once this runs, the send shows up in Mixpanel next to other product events, so the funnel reflects the full picture instead of a gap where the journey step doesn’t report. --- # OpenAI: generate text **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/openai-generate-text/ Ask an OpenAI model to generate a short piece of text in a customer journey, and reuse it in a later push, email, or SMS. --- Use **OpenAI: generate text** to ask an OpenAI model for a short piece of text mid-journey, then reuse it in a later push, email, or SMS. This point authenticates with its own OpenAI API key. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Personalize a message line:** draft a one-line hook from a traveler’s tags, then drop it into the next push or email with [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Summarize before sending:** turn a few tags, such as last order or browsing category, into a short natural-language line a template can display. * **Vary the wording:** ask for a slightly different phrasing on each run so travelers don’t see identical text every time they pass through. ## Configure the point 1. Drag **OpenAI: generate text** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **OpenAI API key**, enter your OpenAI API key. It’s stored masked, like a webhook secret header. ![OpenAI generate text drawer with Step Name filled and OpenAI API key masked](https://docs.pushwoosh.com/journey-elements-openai-generate-text-1.webp) 4. In **Model**, choose the OpenAI model to use. Defaults to **gpt-5-mini**. * **gpt-5-nano** and **gpt-5-mini:** both answer well within the point’s 10-second timeout. * **gpt-5:** can run close to the timeout on a long prompt. Costs more per token than the smaller models. Every call bills your own OpenAI key for every traveler who reaches this step. ![Model field set to gpt-5-mini with the per-model timing note](https://docs.pushwoosh.com/journey-elements-openai-generate-text-2.webp) 5. In **Prompt**, write what you want the model to generate. It takes the same macros as any Webhook body, for example `{{device:hwid}}`. ![Prompt field with a product recommendation prompt using a tag macro](https://docs.pushwoosh.com/journey-elements-openai-generate-text-3.webp) 6. Click **Save**. Reasoning is set to minimal and responses are capped at about 300 tokens (roughly a short paragraph) to keep the request inside the [Webhook timeout](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests). Ask for something that short. A longer or more detailed prompt doesn’t get more room to answer. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ------------------- | ------------------------------------ | | `llm_text` | The generated text | | `llm_input_tokens` | Token count for the request’s input | | `llm_output_tokens` | Token count for the request’s output | ### How to use `llm_text` in later steps * **Drop it into a push, email, or SMS:** use [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/). * **Branch on it with [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/):** save it to a Tag first with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), then split on that Tag. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). A response that comes back but isn’t what you expected keeps the traveler in the journey: * **Text cut off at the 300-token cap:** `llm_text` holds the truncated text. * **Model declined to answer:** `llm_text` holds whatever it replied with instead. ## Example scenario: Drafting a browse-abandonment email line with OpenAI A team wants a browse-abandonment email to open with a line about the specific product a visitor looked at, instead of a generic “come back” subject. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `ProductViewedNoPurchase` event. 2. Add **OpenAI: generate text** right after it. In **Prompt**, write `Write one short sentence recommending {{tag:Last Viewed Product}} to a shopper who looked at it but didn't buy.` 3. Add a **Send Email** step right after it, using [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) to insert `llm_text` into the email body. Once this runs, each visitor gets an email opening line about the exact product they viewed, instead of one generic line for the whole segment. --- # Salesforce: create or update record **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/salesforce-upsert-record/ Create or update a Salesforce record from a customer journey step, matched by an external id field, using a connected app. --- Use **Salesforce: create or update record** to upsert one Salesforce record from a journey step, matched on an external id field: it updates the record when the value is already there and creates it when it isn’t. Salesforce has no static API token, so this point authenticates through a connected app registered once for the whole account. There’s no token field on the point itself. Typical uses: * **Keep Salesforce in sync:** create or update a Contact or Lead as the traveler moves through the journey, so sales sees current data. * **Sync journey activity into CRM:** set a field when a traveler reaches a milestone, so sales can filter or report on it. * **Avoid duplicate records:** matching by external id means a traveler who passes through this point again updates the same record instead of creating a new one. ## Before you start Create a connected app in your Salesforce org and connect it under **Settings > 3rd-party integrations > Salesforce**: 1. In Salesforce, create an **External Client App** (or a classic connected app) with OAuth enabled and the **Manage user data via APIs (api)** scope. 2. Enable the **Client Credentials Flow** on it and pick the run-as user. The point writes as that user, so its profile decides which objects and fields it may touch. 3. In Pushwoosh, in **My Domain host**, enter your org’s host without `https://`, for example `acme.my.salesforce.com`. 4. In **Consumer Key** and **Consumer Secret**, paste the app’s credentials. The secret is stored write-only: Pushwoosh never shows it again, so paste a new one to replace it. 5. Click **Connect Salesforce**. ![Settings Salesforce connect form with My Domain host, Consumer Key, and Consumer Secret fields](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-1.webp) After you connect Salesforce, [install the integration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#install-an-integration) so the point appears in the journey builder. Two things also need to be in place on the Salesforce side: * The field you match on has to be marked **External ID** on that object. A plain text field, even `Email`, isn’t enough — Salesforce refuses the request with `NOT_FOUND` when the field isn’t an external id. * The connected app’s Run As user needs field-level access to that field. A field created through Setup or the API grants nothing by itself, and without the permission Salesforce answers with the same `NOT_FOUND: Field name provided ... does not match an External ID` — the field is invisible to that user, so from the API’s side it does not exist. Add it to a permission set assigned to the Run As user. Note A field marked External ID and Unique is the usual choice for a device or user identifier, for example a custom `Pushwoosh_HWID__c` on Contact. ## Configure the point 1. Drag **Salesforce: create or update record** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **My Domain host**, enter the same host the connected app lives on. ![Salesforce create or update record drawer with Step Name filled and My Domain host set to a Salesforce domain](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-2.webp) 4. In **Object**, enter the object’s API name — `Contact`, `Lead`, or a custom object ending in `__c`. ![Object field set to Contact](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-4.webp) 5. In **External id field**, enter the API name of the field marked External ID. ![External id field set to Pushwoosh_HWID__c](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-5.webp) 6. In **External id value**, enter the value to match on, usually a macro like `{{device:hwid}}`. ![External id value field set to a device hwid macro](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-6.webp) 7. In **Fields**, add one row per Salesforce field to set. Enter the field’s API name and the value. Names must be non-empty and unique. Required fields of the object have to be among them, or Salesforce refuses to create a new record. A value can be fixed text or a traveler macro such as `{{device:hwid}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Fields name and value table with Email mapped to a tag macro](https://docs.pushwoosh.com/journey-elements-salesforce-upsert-record-7.webp) Example With **Object** set to `Contact`, **External id field** to `Pushwoosh_HWID__c`, and **External id value** to the traveler’s device ID (`{{device:hwid}}`), the point matches an existing Contact or creates a new one. Setting **Fields** to `Email` with the traveler’s email (`{{tag:Email}}`) fills in that field. 8. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ---------------------- | --------------- | | `salesforce_record_id` | The record’s ID | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to reference this specific record later, for example through Salesforce’s own API. ### Failures and unexpected responses Open [Calls log](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) in the point’s drawer and check the response: * **401** — the connected app’s credentials are wrong, or the client credentials flow isn’t enabled on it. * **NOT_FOUND** in the body — the object name, or the external id field, doesn’t match anything on that org. Check that the field is really marked External ID. * **REQUIRED_FIELD_MISSING** in the body — creating a new record needs a field the point doesn’t send. Add it in **Fields**. * **INVALID_FIELD** in the body — a name in **Fields** isn’t an API name on that object. A custom field’s API name ends in `__c`. Caution The point sends its upsert as a composite request, and a composite call answers **200 even when the record wasn’t written**: the verdict lives in the body, under `compositeResponse[0].httpStatusCode` and then `compositeResponse[0].body[0].success`. A rejected record therefore doesn’t stop the traveler — it moves on as if the write succeeded, so check the Calls log when records don’t show up. A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Routing an in-app advisory request to sales A wellness app lets users tap **Talk to an advisor** in-app. The team wants that request to become a Salesforce Contact a rep can follow up on. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on an `AdvisorCallRequested` event. 2. Add **Salesforce: create or update record** right after it. Set **Object** to `Contact`, **External id field** to `Pushwoosh_HWID__c`, and **External id value** to `{{device:hwid}}`. In **Fields**, set `Email` to `{{tag:Email}}`. 3. Click **Save**. Once this runs, every in-app advisor request lands in Salesforce as a Contact, matched by device so a repeat tap updates the same Contact instead of creating a duplicate. --- # Slack: post message **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/slack-post-message/ Post a message to a Slack channel from a customer journey step, using your Slack app's bot token. --- Use **Slack: post message** to notify your team in Slack from a journey step. This point authenticates with its own bot token. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Alert on high-value conversions:** post when a traveler completes a purchase or hits a goal, so the team sees it in real time. * **Flag a step that needs a human:** notify support or sales when a traveler reaches a point a bot can’t finish. * **Track journey health:** post a line each time a cohort finishes a branch, without building a dashboard. ## Configure the point 1. Drag **Slack: post message** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Bot token**, enter your Slack app’s `xoxb-` bot token. It’s stored masked, like a webhook secret header, and Slack never returns it back to Pushwoosh. ![Slack post message drawer with Step Name filled and Bot token masked](https://docs.pushwoosh.com/journey-elements-slack-post-message-1.webp) 4. In **Channel**, enter the channel ID or name your app is a member of. ![Channel field set to a marketing-alerts channel name](https://docs.pushwoosh.com/journey-elements-slack-post-message-2.webp) 5. In **Message**, enter the text to post. It takes the same macros as any Webhook body: `{{device:hwid}}`, `{{device:user_id}}`, `{{tag:Name}}`. ![Message field with an order alert using tag macros](https://docs.pushwoosh.com/journey-elements-slack-post-message-3.webp) 6. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ------------------ | ------------------------------ | | `slack_message_ts` | The posted message’s timestamp | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to reference this specific message later, for example when troubleshooting delivery. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Alerting the team about a high-value order A team wants a Slack heads-up the moment a high-value order comes in, without watching Pushwoosh directly. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `HighValueOrderPlaced` event. 2. Add **Slack: post message** right after it. Post to `#order-alerts` with `{{tag:Name}} just placed a {{tag:Order Value}} order`. 3. Click **Save**. Once this runs, the team sees a Slack message in `#order-alerts` the moment a high-value order comes in. --- # Snowflake: insert row **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/snowflake-insert-row/ Insert a row into a Snowflake table from a customer journey step, using a programmatic access token. --- Use **Snowflake: insert row** to write a row into a Snowflake table from a journey step, through the SQL API, using a programmatic access token. Column values travel as bound parameters, not inside the SQL statement text, so a value can’t break out of its column and change what the statement does. This point authenticates with its own programmatic access token. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Feed a reporting table:** insert a row per journey step reached, for a dashboard or scheduled query downstream. * **Log events as they happen:** write raw journey data straight into the warehouse instead of waiting on a batch pipeline. * **Join with existing data:** insert a device or tag value your Snowflake tables can join against your own datasets. Caution A programmatic access token lives for 15 days by default. The point starts failing the day it expires, with no warning beforehand. Put a reminder in your own calendar to rotate it, or requests will silently stop working two weeks after you set this up. ## Configure the point 1. Drag **Snowflake: insert row** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Account host**, enter your Snowflake account’s host from its URL, for example `ab12345.eu-central-1.snowflakecomputing.com`. Don’t include `https://` or a trailing slash. Pushwoosh adds `https://` and the API path itself. ![Snowflake insert row drawer with Step Name filled and Account host set to a Snowflake URL](https://docs.pushwoosh.com/journey-elements-snowflake-insert-row-1.webp) 4. In **Programmatic access token**, enter a token from **Snowsight > your user > Settings > Authentication > Programmatic access tokens**. It’s stored masked, like a webhook secret header. ![Programmatic access token field masked](https://docs.pushwoosh.com/journey-elements-snowflake-insert-row-2.webp) 5. In **Table**, enter the target table as `TABLE`, `SCHEMA.TABLE`, or `DATABASE.SCHEMA.TABLE`, using only letters, digits, and underscores. This point doesn’t ask for a warehouse, database, or schema separately: a bare `TABLE` resolves against whatever database and schema the token’s user defaults to, and a `DATABASE.SCHEMA.TABLE` value overrides them. ![Table field set to PW.PUBLIC.TRAVELERS](https://docs.pushwoosh.com/journey-elements-snowflake-insert-row-3.webp) 6. In **Columns**, add one row per table column to write. Enter the column name and the value. Names must be non-empty and unique, and take the same letters/digits/underscore rule as **Table**. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Columns name and value table with DEVICE_ID mapped to a device hwid macro](https://docs.pushwoosh.com/journey-elements-snowflake-insert-row-4.webp) Example With **Table** set to `PW.PUBLIC.TRAVELERS`, setting **Columns** to `DEVICE_ID` with the traveler’s device ID (`{{device:hwid}}`) inserts a row into that table’s `DEVICE_ID` column. 7. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ---------------------------- | -------------------------------- | | `snowflake_statement_handle` | The submitted statement’s handle | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to check that statement’s status later through Snowflake’s own API. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Streaming journey activity into Snowflake A team wants journey activity in the same Snowflake table their own reporting already queries. 1. Add **Snowflake: insert row** right after the step you want to log. Set **Table** to `PW.PUBLIC.TRAVELERS`. In **Columns**, set `DEVICE_ID` to `{{device:hwid}}`. 2. Click **Save**. Once this runs, the row lands in the same table the team’s Snowflake reporting already reads from. --- # Supabase: insert row **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/supabase-insert-row/ Insert a row into a Supabase table from a customer journey step, using a service role key. --- Use **Supabase: insert row** to write a row into a Supabase table from a journey step, through your project’s REST API, using a service role key. This point authenticates with its own service role key. It doesn’t use any account-level integration configured elsewhere in Settings. Typical uses: * **Write into your app’s own database:** insert a row your app already reads from that Supabase table. * **Log events as they happen:** write raw journey data straight into the table instead of waiting on a batch pipeline. * **Sync with existing data:** insert a device or tag value your Supabase tables can join against your own data. Caution A service role key bypasses row-level security. If your table has RLS policies, don’t use the project’s anon key here — it doesn’t have the rights to insert, and the request fails. ## Configure the point 1. Drag **Supabase: insert row** from the **Integrations** section of the left panel onto the canvas. 2. Double-click the point and enter a **Step Name**. 3. In **Project host**, enter your project’s host from its URL, for example `abcdefghij.supabase.co`. Don’t include `https://` or a trailing slash. Pushwoosh adds `https://` and the API path itself. ![Supabase insert row drawer with Step Name filled and Project host set to a Supabase URL](https://docs.pushwoosh.com/journey-elements-supabase-insert-row-1.webp) 4. In **Service role key**, enter the key from **Project Settings > API > service_role**. It’s stored masked, like a webhook secret header, and Supabase never returns it back to Pushwoosh. ![Service role key field masked](https://docs.pushwoosh.com/journey-elements-supabase-insert-row-2.webp) 5. In **Table**, enter the target table’s name. ![Table field set to travelers](https://docs.pushwoosh.com/journey-elements-supabase-insert-row-3.webp) 6. In **Columns**, add one row per table column to write. Enter the column name and the value. Names must be non-empty and unique. A value can be fixed text or a traveler macro such as `{{tag:Name}}`, the same macros as in a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#use-dynamic-data-and-macros) body. Or click **Insert Dynamic Value** to pick an attribute an earlier step in this journey produced, for example an LLM response or a mapped Webhook value. ![Columns name and value table with device_id mapped to a device hwid macro](https://docs.pushwoosh.com/journey-elements-supabase-insert-row-4.webp) Example With **Table** set to `travelers`, setting **Columns** to `device_id` with the traveler’s device ID (`{{device:hwid}}`) inserts a row into that table’s `device_id` column. 7. Click **Save**. ## Response The traveler continues to whatever step you connect next on the canvas. ### Attributes after the step runs | Attribute | What it holds | | ----------------- | ---------------- | | `supabase_row_id` | The new row’s id | ### How to use it in later steps The traveler already continued to the next step regardless of this attribute. Save it to a Tag with [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) if you need to reference or update this specific row later, for example through Supabase’s own API. ### Failures and unexpected responses A failed request drops the traveler from the journey. See [Errors and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/#errors-and-failed-requests). ## Example scenario: Logging a new trial in Supabase An app wants every trial start recorded in its own Supabase database, without a separate export job. 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and set it to fire on a `TrialStarted` event. 2. Add **Supabase: insert row** right after it. Set **Table** to `travelers`. In **Columns**, set `device_id` to `{{device:hwid}}`. 3. Click **Save**. Once this runs, every trial start gets a row in the travelers table, ready for the app’s own queries. --- # Using webhook response data in your journey **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/ Turn a Webhook element's response into variables and use them in Update user profile or Time Delay elements later in the journey. --- ## Overview When you [map values from a webhook response](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables), Pushwoosh stores them as variables you can use later in the journey in **Update user profile**, **Time Delay**, and content personalization, the same way you’d use an [Event attribute](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#2-set-attributes). The one exception is **Condition split**: it can’t branch on a webhook variable directly, because it needs a value with a declared type (a Tag or an Event attribute), and a mapped webhook value has no type. See [Compare a webhook value in Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/#compare-a-webhook-value-in-condition-split) below. Note To add a **Webhook** element and configure **Response mapping**, see [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables). ## Save a webhook value as a Tag with Update user profile A [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) can return data from an external system, such as a CRM user ID or subscription status, that Pushwoosh does not store on the user profile yet. [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response) saves mapped values as [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) so you can use them in [segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/), [dynamic content](https://docs.pushwoosh.com/product/personalization/dynamic-content/), and later journey steps. ### Example: save a CRM user ID as a Tag A new user signs up in your app, and the journey creates a matching record in your CRM. Saving the returned CRM user ID as a Tag lets you reference or update the same CRM record later, for example from another journey or a follow-up Webhook call, instead of creating a duplicate record each time the user re-enters the journey. **Journey flow:** Trigger-based Entry → Webhook → Update user profile → end the branch 1. Create a journey with **Trigger-based Entry** on the `SignUp` event. 2. Add a **Webhook** element after the entry step. Name it (for example, `Create user in CRM`) and set the request URL to your CRM create-user endpoint. 3. In **Response mapping**, set **Path** to the ID field in the response (for example, `data.user.id`) and **Attribute** to `crm_user_id`. ![Webhook response mapping with Path data.user.id and Attribute crm_user_id](https://docs.pushwoosh.com/journey-elements-webhook-response-data-1.webp) 4. Add an **Update user profile** element after the **Webhook** step. 5. In **Dynamic Tag Value**, click **+ Dynamic Value**. In **Tag**, select the Tag that stores the CRM ID (create it in advance if needed). In **Event**, select the webhook step by the name you gave it (`Create user in CRM`). In **Dynamic Value**, select `crm_user_id`. ![Dynamic Tag Value with Tag for CRM ID, Event Webhook step, and Dynamic Value crm_user_id](https://docs.pushwoosh.com/journey-elements-webhook-response-data-2.webp) 6. End the branch — add an **Exit** element or leave it without an outgoing connection. ![Journey branch ending with Exit after Update user profile step](https://docs.pushwoosh.com/journey-elements-webhook-response-data-3.webp) Caution If the webhook value is missing for a user (the response wasn’t JSON, the **Path** didn’t resolve, or the call failed), Pushwoosh skips that Tag update. The Tag keeps its previous value or stays unset, and the journey continues with no error at this step. Run [**Test webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#test-the-webhook) before launch to confirm the request succeeds, then compare the response body with your **Path** in [**Calls log**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log). [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response). ## Schedule a delay from a webhook value with Time Delay Visit dates, renewal deadlines, and delivery windows often live in an external booking or billing system. After a [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-webhook-response-data-to-variables) maps the date from the API response, [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response) can pause the journey until that moment, for example 2 days before an appointment, so the next message goes out on time. ### Example: schedule a reminder from a date in the response A user books an appointment in your app. The journey fetches the visit date from your booking system and sends a push reminder 2 days before the visit. 1. Create a journey with **Trigger-based Entry** on the `AppointmentBooked` event. 2. Add a **Webhook** element after the entry step. Name it (for example, `Get appointment details`) and set the request URL to your booking system API. 3. In **Response mapping**, set **Path** to the date field in the response (for example, `appointment.date`) and **Attribute** to `visit_date`. ![Webhook step with response mapping for appointment.date to visit_date](https://docs.pushwoosh.com/journey-elements-webhook-response-data-4.webp) 4. Add a **Time Delay** element after the **Webhook** step. Select **Based on user/event data**, set **Get date from** to **Event**, set **Event** to the webhook step by the name you gave it (`Get appointment details`), set **Event value** to `visit_date`, and set the delay to **Before** `2` **Days**. ![Time Delay configured from webhook visit_date with Before 2 Days offset](https://docs.pushwoosh.com/journey-elements-webhook-response-data-5.webp) 5. If users can book less than 2 days before the visit, enable **Split to branches if the date’s in the past or date is empty** on the **Time Delay** element. This creates two branches, **In the past** and **In the future**. See [Split branches when the date is in the past or empty](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#split-branches-when-the-date-is-in-the-past-or-empty). 6. Add a **Push** step with your reminder message. If split is enabled, add this **Push** in both branches: **In the past** (send immediately) and **In the future** (after **Time Delay**). 7. End each branch — add an **Exit** element or leave it without an outgoing connection. ![Time Delay split into In the past and In the future branches, each with Push and Exit steps](https://docs.pushwoosh.com/journey-elements-webhook-response-data-6.webp) Caution If the mapped value is missing or not a valid date, the user exits the journey with an error at this step. If the visit is sooner than 2 days away, the **Before** `2` **Days** target is already in the past when the user reaches **Time Delay**, and the user leaves the journey with no error and no reminder unless you enable **Split to branches if the date’s in the past or date is empty**. Unlike [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), which leaves the Tag unchanged and continues, **Time Delay** stops the user when the date is missing, invalid, or in the past (without branch splitting). Make sure your webhook reliably returns a date in a [supported format](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#delay-based-on-user-or-event-data). Run [**Test webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#test-the-webhook) before launch. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/#use-a-date-from-a-webhook-response). ## Compare a webhook value in Condition split **Condition split** branches on a Segment, a Tag, or an Event attribute with a declared type. A value mapped from a webhook response has no type, so it doesn’t appear in **Condition split**’s dropdowns and can’t be compared directly. To branch on a webhook value, first save it as a Tag of the matching type with [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/#use-a-value-from-a-webhook-response), then compare that Tag in **Condition split**. ### Example: branch on a webhook balance against the triggering event’s amount A user requests a purchase, and the journey checks whether their CRM balance covers the price from the triggering event before continuing. **Journey flow:** Trigger-based Entry → Webhook → Update user profile → Condition split 1. Create a journey with **Trigger-based Entry** on the `PurchaseRequested` event, with a numeric `required_amount` attribute. 2. Add a **Webhook** element after the entry step. In **Response mapping**, set **Path** to the balance field in the response (for example, `current_balance`) and **Attribute** to `current_balance`. 3. Add an **Update user profile** element after the **Webhook** step. In **Dynamic Tag Value**, set **Tag** to a numeric Tag of type Integer or Price (create it in advance if needed, for example `Current_balance`), **Event** to the webhook step, and **Dynamic Value** to `current_balance`. 4. Add a **Condition split** element after **Update user profile**. Set the condition to Tag `Current_balance`, operator **greater or equals**, and for the value select **Event attribute** → the entry event → `required_amount` instead of a constant. 5. Connect each branch to the next steps. Note The Tag and the Event attribute must be the same type (both numeric) for the comparison to work. ## Things to keep in mind * **Path syntax:** use a dot-separated path (for example, `data.user.id`), or a `*` segment to map every element of an array. Filters aren’t supported. See [Map every element of an array](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-every-element-of-an-array) for details. * **Response size:** responses larger than 64 KB are not processed for mapping. * **Condition split:** a mapped webhook value has no type and can’t be used directly. Save it as a Tag first. See [Compare a webhook value in Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/#compare-a-webhook-value-in-condition-split). * **Testing:** run [**Test webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#test-the-webhook) in the **Webhook** element before you use mapped values in a live journey. Confirm the request succeeds, then check the response body against each **Path** in the [**Calls log**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log) tab. If a value looks wrong for a specific traveler once the journey is live, check that traveler’s row in **Calls log**. * **Lists in the response:** use `*` in **Path** to map every item. Choose `item_{n}` for separate attributes or a name without `{n}` for one comma-separated string. **Path** indexes start at 0. `{n}` names start at 1. At most the first 50 elements are mapped. See [Map every element of an array](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#map-every-element-of-an-array). --- # Journey history and versions **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/ Learn what a journey version is, why Pushwoosh creates one, and how to read the change log, view versions, and manage them. --- When you edit a running journey, you need to know what changed and what happens to people already inside it. In Pushwoosh, open the journey’s **History** for both: * **Change log:** what changed and who changed it * **Versions:** people already in the journey stay on the old path when an edit shouldn’t apply to them, while new users get the update ## Open journey history 1. Open **Campaigns**. 2. Open the journey you want to check. 3. On the canvas, click the clock icon at the bottom-right to open the **History** drawer. ![Clock icon in the canvas toolbar that opens the history drawer, next to Notes, Canvas settings, and Documentation](https://docs.pushwoosh.com/customer-journey-journey-versioning-8.webp) ## Journey versions ### What is a journey version A journey version is a snapshot of the campaign, created the moment you resume it. If people are already in a paused journey and your edit can’t apply to their run, Pushwoosh locks that run in place as its own version when you click **Resume campaign**, and creates a new version for the edit. The version isn’t created while you’re editing. See [Why Pushwoosh creates a new version](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#why-pushwoosh-creates-a-new-version) for which edits do this. Both versions stay linked to the same campaign. They appear as one entry in the [campaigns list](https://docs.pushwoosh.com/product/customer-journey/journeys-list/), but each has its own status, its own users, and its own history. To check which version a specific person went through, use **Find user path** in the canvas toolbar and search by User ID, HWID, email, phone, or token. If the journey has more than one version, the result names the version their path belongs to. Journey versioning is on by default. If your account doesn’t have it yet, the drawer opens straight to the change log with no **Versions** tab, and resuming a paused journey applies your edit to the same run instead of creating a new version. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you don’t see the **Versions** tab. #### Why Pushwoosh creates a new version A structural edit to a paused journey, such as changing the canvas, the audience, or a branching condition, would redirect users already mid-journey if applied to the version they’re in. Versioning prevents that: the edit becomes a new version that only new users enter, while everyone already inside keeps going through the version they originally entered. Not every edit requires a new version. A wording change to a message, for example, can stay on the current one. What happens when you click **Resume campaign** depends on what you changed. See [What happens when you resume a paused journey](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#what-happens-when-you-resume-a-paused-journey). ### View versions In the **History** drawer, select the **Versions** tab. ![History drawer open on the journey canvas with the Versions tab selected next to Change log](https://docs.pushwoosh.com/customer-journey-journey-versioning-2.webp) Each version card shows: * A **Version N** badge and a status (**Active**, **Paused**, or **Inactive**) * **Open for entries** or **Closed for entries**, and how many users are still going through it * The date it was activated and who activated it What **Open** and **Closed** mean: * **Open for entries:** new users can still enter this version. * **Closed for entries:** new users can’t enter it. A version closes when a newer version launches, or when you stop it. People already inside keep going through it. Click a card to open that version’s canvas. When the journey has two or more versions, use the version selector in the top bar to switch between them. ![Journey canvas top bar with the version selector for switching between existing versions](https://docs.pushwoosh.com/customer-journey-journey-versioning-4.webp) Important Only the newest version can be edited or resumed. On an older version, **Resume campaign** is disabled. Make changes on the current version instead. ### What happens when you resume a paused journey Editing a paused journey doesn’t create a new version by itself. What happens next depends on what you changed when you click **Resume campaign**. The edit lists below are examples, not an exhaustive catalog of every field. You don’t need to check your edit against them. Click **Resume campaign** and see which dialog opens, if any. That tells you which case you’re in. #### No edits, or only moving an element or a note For example: no changes since the pause, or only moving an element on the canvas or editing a note. Click **Resume campaign**. The journey resumes on the same version, and no new version is saved. #### Edits that can’t apply to the current version Some edits can’t apply to people already on the current version. Pushwoosh saves them as a new version instead. For example: * Canvas structure (elements, connections, element type) * Who enters or how (audience, trigger, entry rules) * Path, timing waits, goals, or campaign entry limit * Application on a message element, message type / unsubscribed policy, or webhook setup 1. Click **Resume campaign**. 2. Choose what happens to people still on the current version. Set **Stop current version permanently**: * **Off** (default): they keep going through the current version. * **On:** they stop receiving messages from that version right away. ![Resume in new version dialog with Stop current version permanently toggle and user count](https://docs.pushwoosh.com/customer-journey-journey-versioning-6.webp) 3. Click **Resume campaign** to save the new version. New users enter the new version. Users already in the journey stay on the version they entered, unless you stopped that version in step 2. #### Edits where you choose new version or current Some edits don’t force a new version: * **Message content:** preset, text, subject line, or personalization * Frequency capping, send rate, or optimal send time * Journey title or Silence period time windows 1. Click **Resume campaign**. 2. Choose how to apply the edits: * **Launch new version** (default): new users get the updated journey. People already inside finish on the version they entered. * **Continue on current version:** everyone (new and existing) goes through the updated journey on the same version. No new version is saved. ![Resume campaign dialog with Launch new version selected and Stop current version permanently toggle](https://docs.pushwoosh.com/customer-journey-journey-versioning-1.webp) 3. If you picked **Launch new version**, set **Stop current version permanently** the same way as in [Edits that can’t apply to the current version](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#edits-that-cant-apply-to-the-current-version). 4. Click **Resume campaign** to confirm. If the journey uses [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), either resume path can show additional audience options, the same ones covered in [Resume options for Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#resume-options-for-audience-based-entry): which entry points are new-only or scheduled when launching a new version, or whether to re-enter each audience source when continuing on the current version. ### Stop a version 1. Open the [Versions tab](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/#view-versions). 2. Click the three-dot menu on a version’s card. 3. Select **Stop this version…**. ![Version card three-dot menu open with the Stop this version option](https://docs.pushwoosh.com/customer-journey-journey-versioning-7.webp) Important Users still in that version stop receiving messages immediately. This can’t be undone for that version. * Stopping the newest version also stops new users from entering the campaign. Other versions keep running as usual. * Stopping an older version only affects users still in that version. If the campaign becomes **Inactive** after you stop a version, restore it with **Restore** in the campaigns list or **Restore to edit** on the canvas. See [Restore](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#restore). ## Change log Use the **Change log** when you need to see what changed in the journey, when it changed, and who changed it. For example, check an update or find who edited a step. It is not a separate run of the campaign. Many edits appear here without creating a new version. In the **History** drawer, select the **Change log** tab. ![History drawer Change log tab with expanded entry listing campaign and element updates](https://docs.pushwoosh.com/customer-journey-journey-versioning-5.webp) Each entry shows when the update happened, who made it, and what was modified (for example, campaign launch or entry conditions). Expand an entry to see the changes on an individual journey element. ![History drawer Change log tab listing Campaign paused, Campaign launched, and Promo Updated entries](https://docs.pushwoosh.com/customer-journey-journey-versioning-9.webp) --- # Journey settings **URL:** https://docs.pushwoosh.com/product/customer-journey/journey-settings/ Configure journey settings for conversion goals, silence periods, entry limits, and cloning to control behavior and results --- ## Campaign settings Campaign settings allow you to set rules that apply to the entire Journey. You can access these settings by clicking **Settings** on the journey canvas. ### Conversion goals **Conversion Goals** let you measure the effectiveness of your customer journey. A Goal is a target [Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) indicating that the customer reaches the marketing objective you set. To define the journey’s goals, click **Settings** on the canvas and select **Conversion goals**. ![Conversion goals panel in journey settings](https://docs.pushwoosh.com/customer-journey-journey-settings-1.webp) 💡 How conversion goals are calculated Conversion Goals in Customer Journey are attributed to the specific message step that influenced the goal, whether directly (after opening a message) or indirectly (after receiving a message). This provides actionable insights into which steps in your journey drive user behavior. ##### Attribution logic * A Conversion Goal is counted only if the user remains in the journey or within the defined conversion period. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings#specify-the-conversion-period) * If the goal is achieved after the user opens a message (e.g., a push notification or email), it is attributed to the **last opened communication step** before the goal was achieved. * If there were no message openings, the goal is attributed to the **last sent communication step** before the goal was achieved. This logic ensures that Conversion Goals are attributed to the message steps that most significantly influenced the user’s behavior. By linking goals to the most impactful steps, you can better understand which parts of your journey drive conversions and use these insights to optimize your campaigns for maximum effectiveness. Additionally, this attribution method provides more actionable data by associating goals with the most relevant communication steps. ##### Example scenario **Scenario A** Sarah receives a push notification about a special offer. She opens the notification, visits the app, and completes a purchase, fulfilling the Conversion Goal. **Attribution:** The Conversion Goal is tied to the **push notification step** since Sarah opened the message and it influenced her behavior. **Scenario B** John receives the same push notification but does not open it. Later, he independently visits the app and completes a purchase, fulfilling the Conversion Goal. **Attribution:** The Conversion Goal is tied to the **push notification step** as the **last sent communication** before the goal was achieved. #### Specify the conversion period First, specify the **Conversion period**. This allows you to set the time window during which a customer must reach the target event after leaving the campaign. If the customer achieves the set goal within this period, it counts as a conversion. The maximum time period is 30 days. The **Conversion period** is important for evaluating the effectiveness of a campaign. It goes beyond immediate clicks and actions by taking into account conversions that occur over time. This is crucial because users often need time to process campaign messages and take action after their initial interaction. By setting a conversion period, you can capture these delayed conversions. ![Conversion period setting in journey settings](https://docs.pushwoosh.com/customer-journey-journey-settings-2.webp) Note The length of the conversion period can vary depending on the type of campaign you are running. For example, a short-term, sales-driven campaign may benefit from a one-day conversion window, allowing you to quickly measure immediate responses to your efforts. On the other hand, campaigns such as event registrations may require a longer conversion window of 10 days or more to accommodate the time users need to plan their schedules and register at their convenience. #### Set the target event Then, choose the **Event** you consider the indicator of the journey’s success. ![Select target event for conversion goal](https://docs.pushwoosh.com/customer-journey-journey-settings-3.webp) To specify the Event’s attributes, press the **Add condition** button. Choose an attribute, operator, and value(s). ![Add condition for event attributes](https://docs.pushwoosh.com/customer-journey-journey-settings-4.webp) To cancel remaining messages once a user reaches a Conversion Goal, check the **End journey after this event** checkbox. Messages that haven’t been sent to the moment the goal is reached won’t be sent to the customer who reached the goal. Note When several goals are set, a user who reaches **any of them** ends the journey at that point. ![Conversion goal example displayed on canvas](https://docs.pushwoosh.com/customer-journey-journey-settings-5.webp) Press **Apply** to save the goal setup. When the journey is active, click **Settings** and select **Conversion goals** to see the stats. ![Conversion goals statistics view](https://docs.pushwoosh.com/customer-journey-journey-settings-6.webp) For each element users can reach the goal at, the count of goals reached is displayed in the element’s statistics shown by hovering the journey element over. ![Element hover showing goal count](https://docs.pushwoosh.com/customer-journey-journey-settings-7.webp) ### Silence period To avoid sending messages at inconvenient times, set a **Silence period** to pause notifications, emails, and other messages for Customer Journey participants. For this: 1. Click **Settings** on the canvas and select **Silence period**. ![Silence period settings in journey settings](https://docs.pushwoosh.com/customer-journey-journey-settings-8.webp) 2. Toggle the switch next to the channel you want to apply the Silence period to (e.g., Push notifications, emails, SMS, WhatsApp, in-apps). ![Select channel for silence period](https://docs.pushwoosh.com/customer-journey-journey-settings-9.webp) 3. Set the time range for pausing message delivery. For example, 23:00 – 08:00 will stop messages from being sent during these hours. This period will repeat at the same time every day based on the subscriber’s device timezone. 4. You can also customize the schedule for each day. Select **Pause for entire day** next to a specific day to stop messaging for a full 24 hours. On other days, messages will be paused only during the defined time range. ![Customize schedule by day](https://docs.pushwoosh.com/customer-journey-journey-settings-10.webp) 5. Choose message behavior when Silence period ends: * **Send after Silence Period ends:** Users will receive the message as soon as the Silence period ends. * **Skip message and continue journey:** Users will not receive the message and will immediately move to the next step in the Customer Journey. * **Skip message and wait until Silence Period ends:** Users will not receive the message, and their progress in the Customer Journey will be delayed until the Silence period ends. ![Choose message behavior after silence period](https://docs.pushwoosh.com/customer-journey-journey-settings-11.webp) 6. Click **Save** to apply the changes. ### Campaign entry limit **Campaign entry limit** defines how many times the same user can enter a journey within a period you choose. Events that start journeys can repeat, even several times a day, and audience segments update regularly. Without a limit, a user can enter the same active journey again and again and get too many messages. Example A user adds a product to their cart and enters the journey. Without a limit, the same user enters again if they add another product after leaving the journey. With the limit set to a day, they don’t re-enter within 24 hours after entering. To set the entry limit: 1. Click **Settings** on the canvas and select **Campaign entry limit**. ![Settings dropdown menu with Campaign Entry Limit option highlighted](https://docs.pushwoosh.com/customer-journey-journey-settings-25.webp) 2. In the **Campaign Entry Limit** window, choose one of the options: * **No limits:** users can enter the journey without any limitations. * **Custom period:** users can enter the journey once within the period you select in the **Once in** field. ![Campaign Entry Limit window with Custom period selected and Once in set to a day](https://docs.pushwoosh.com/customer-journey-journey-settings-12.webp) 3. For **Custom period**, select a period in the **Once in** field: * **a lifetime:** the user enters the journey only once ever * **a day** (24 hours) * **a week** (7 days) * **a month** (about 30 days) * **3 months** (about 91 days) 4. Click **Apply**. In a journey that is already running, click **Save** instead. Note Campaign entry limit periods are counted off from the moment a user enters the journey. ### Multiple sessions If a [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) uses [multiple active sessions](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#multiple-active-sessions-per-user), a **Multiple sessions** option appears on the journey canvas. Click **Settings** and select **Multiple sessions** to open a drawer that shows every point in the journey where a session-matching attribute is required, so you can review and adjust those attributes in one place. ![Multiple sessions option in Settings dropdown for managing session-matching attributes across journey points](https://docs.pushwoosh.com/customer-journey-journey-settings-21.webp) The drawer consolidates information from: * **[Trigger-based entry points](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/)** * **[Wait for Trigger steps](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/)** * **[Conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals)** ![Multiple sessions settings drawer showing session-matching attributes for entry points, trigger steps, and conversion goals](https://docs.pushwoosh.com/customer-journey-journey-settings-22.webp) For each point, you can: * View the session-matching attribute currently used (e.g., `order_id`) * Select a different attribute directly in the drawer * See which events are associated with that point ### Cancel events: Remove users from a journey without counting a conversion Cancel events are conditions that immediately remove users from a Journey without being counted as conversions. These events are used solely to exit users and do not impact your conversion statistics. ##### Use cases Set up cancel events for actions that should stop further messaging but aren’t considered a campaign success. For example, unsubscribing from emails or disabling push notifications. These events help you maintain cleaner user flows and avoid counting technical or preference-based actions as conversions. ##### Set up Cancel events To set up a Cancel event: 1. Open your campaign and go to **Settings > Cancel events**. ![Cancel events settings page showing event dropdown and configuration options](https://docs.pushwoosh.com/customer-journey-journey-settings-23.webp) 2. Select an event from the dropdown. 3. Add conditions if needed. 4. Click **Save** to apply the changes. Tip You can add multiple cancel events if needed. ![Cancel event configuration with selected event, conditions added, and Save button](https://docs.pushwoosh.com/customer-journey-journey-settings-24.webp) As soon as a user triggers any of the defined events and matches the conditions, they will be removed from the campaign. To download a list of users who exited the campaign due to a cancel event, go to **Settings > Cancel events** on the Journey canvas. Click the **download icon** next to the event name. The CSV file will include user IDs and the cancel event that triggered their exit. Tip You can see which cancel event caused a user to exit the journey in the [Audience tab](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#audience), under the Exited section, next to the user’s last step. ## Actions with journeys ### Launch Once the journey is set up, press the **Launch campaign** button at the top right of the journey canvas. From that moment, the journey starts tracking users’ behaviors and sending messages to them. ![Launch campaign button on canvas](https://docs.pushwoosh.com/customer-journey-journey-settings-13.webp) ### Edit Both draft and active journeys can be edited. You can update journey settings and content to test communication effectiveness, fix errors, or adapt your campaigns to special events, changing conditions, or new business needs. [More about editing journeys](https://docs.pushwoosh.com/product/customer-journey/journey-editing) ![Journey editing interface](https://docs.pushwoosh.com/shared-58.webp) ### Clone You can duplicate an existing campaign within the same project or clone it to another project while keeping its structure, settings, selected events, segments, and content. Cloning a journey is useful in the following cases: * **Testing in a sandbox environment:** Before running a journey in a live app, you can clone it to a test app to check its logic, triggers, and messaging flow without affecting real users. * **Transferring campaigns across projects:** If you manage multiple apps, cloning allows you to quickly move successful campaigns between projects, saving time and effort. * **Reusing a template:** Create a sample journey that can be customized and reused for different goals or scenarios. * **Restarting a deactivated journey:** Cloning gives you a fresh copy to launch, if you’d rather not [restore](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#restore) the original. ##### How to clone a campaign Tip Watch the video to learn how to clone campaigns, or follow the step-by-step guide below. [Youtube Video: Campaign cloning for copying and reusing your most effective campaigns](https://youtube.com/watch?v=S-ZBQ9_3x2U) 1. You can clone a campaign from either the [**Campaigns list**](https://docs.pushwoosh.com/product/customer-journey/journeys-list/) or the **journey canvas**. * **Journey canvas:** If the campaign is inactive, click the **Clone campaign** button. If the campaign is active, click the campaign status dropdown in the top-right corner and select **Clone** from the menu. This dropdown is labeled **Campaign is active**, or **Active · entry closed** / **Active · entry closed · N in flight** once an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#when-entry-closes)’s entry window has closed. * **Campaigns list:** Click **Clone** next to the campaign name to open the cloning settings. 2. In the window that opens, enter a name for the cloned campaign. 3. Select up to 5 destination projects from the dropdown list. You can clone the journey within the same project or to other projects in your account. 4. Choose what to do with the message content: * **Clear messages content:** Removes all message content from the cloned campaign. You will need to select an existing preset or create new content. * **Keep messages content:** Copies the message content to the cloned campaign. Editing presets will apply changes across all campaigns within the same project. Preset changes don’t sync across projects *** If you clone a campaign to a different project, any changes made to message presets in the original project will not apply to the cloned campaign, and vice versa.You will need to manually update presets or create new content in the destination project after cloning. 5. Click **Clone** to create the duplicate campaign. ![Clone campaign settings dialog with destination projects and content options](https://docs.pushwoosh.com/customer-journey-journey-settings-16.webp) ### Deactivate To stop a journey and mark it as **Inactive**, select **Stop campaign**: * **Campaigns list:** open the three-dot menu next to an **Active** or **Paused** campaign and select **Stop campaign**. * **Journey canvas:** click the **Campaign status** dropdown in the upper-right corner and select **Stop campaign**. Stopping removes all users currently in the campaign; they don’t come back if you restore it. ![Deactivate campaign confirmation](https://docs.pushwoosh.com/customer-journey-journey-settings-17.webp) The campaign will be marked as **Inactive** in the [campaigns list](https://docs.pushwoosh.com/product/customer-journey/journeys-list): ![Campaign marked as inactive in list](https://docs.pushwoosh.com/customer-journey-journey-settings-18.webp) ### Restore Available for **Inactive** campaigns only. To resume editing a stopped campaign, select **Restore**: * **Campaigns list:** open the three-dot menu next to an **Inactive** campaign and select **Restore**. * **Journey canvas:** click the **Campaign status** dropdown in the upper-right corner and select **Restore to edit**. Restoring returns the campaign to **Paused**. It doesn’t bring back the users removed when the campaign was stopped — starting the campaign again enters users anew. ### Archive If you have many inactive journeys, archive them to keep the list easy to navigate. Archived journeys can be cloned like campaigns from the main list. Caution Archiving is irreversible — an archived journey cannot be restored. Clone it if you need to run it again. To archive a journey, go to the [campaigns list](https://docs.pushwoosh.com/product/customer-journey/journeys-list), click the three-dot menu icon next to the inactive campaign, and select **Archive**. ![Archive campaign option in campaigns list menu](https://docs.pushwoosh.com/customer-journey-journey-settings-19.webp) To view archived journeys, go to the campaigns list and click the **Archived** filter. ![Archived journeys list filter](https://docs.pushwoosh.com/customer-journey-journey-settings-20.webp) ## Pre-launch check Click **Launch campaign** (top right of the canvas) to check if the journey is ready. If something is missing, a **Validation** panel opens and lists the issues, for example: * Add at least one entry element * Element is not reachable from entries * Please enable Email platform to use Send Email * An event is set as both a Conversion goal and a Cancel event with overlapping conditions An open branch (one without an outgoing connection) is a valid way to end it, so a missing Exit element is never one of the listed issues. Fix the issues on the canvas, then click **Revalidate** to check again — the list updates in place. Click **Stop** instead to close the panel and keep editing without relaunching. Once the list is empty, the green check on the canvas confirms the journey is ready to launch, with no Exit element in sight: ![Journey canvas with an open branch and a green validity check confirming it's ready to launch without an Exit element](https://docs.pushwoosh.com/customer-journey-journey-settings-26.webp) Click **Launch campaign** again to open a confirmation dialog, then click **Launch** to start sending. ### Overlapping Conversion goal and Cancel event If the same event is set as both a [Conversion goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) and a [Cancel event](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#cancel-events-remove-users-from-a-journey-without-counting-a-conversion), the pre-launch check compares their conditions: * **Conditions overlap, match exactly, or are both empty:** launch is blocked with `Can't launch: event "{event_name}" is set as both a Conversion goal and a Cancel event with overlapping conditions.` The same event would both count as a conversion and remove the user from the journey, which would make your conversion stats inaccurate. To launch, either remove the event from one of the two settings, or add conditions so they don’t overlap, for example Conversion goal `type is Trial, Initial` and Cancel event `type is not Trial, Initial`. * **Conditions keep the two events apart:** launch is allowed, and both settings apply as configured. Note This check runs only when launching a journey. On a campaign that’s already **Active** with the same event in both settings and identical or no conditions, the Cancel event takes priority: the user is removed and not counted as a conversion. --- # Journeys list **URL:** https://docs.pushwoosh.com/product/customer-journey/journeys-list/ Learn how to manage and analyze your customer journeys. View campaign performance, organize journeys by categories, and track metrics --- ## Campaigns list The **Campaigns** page gives you an overview of all customer journeys (campaigns) you have created. Use it to see status and performance at a glance and to manage campaigns. At the top of the page you have: * **Search by name:** search bar to find campaigns by name * **Status filters:** tabs for All, Active, Inactive, Draft, Paused, Archived * **Any category:** dropdown to filter by category * **Show filters:** click to filter by Entry type, Launched date, or sort (e.g. by last modified). * **Export statistics:** download campaign data for the selected date range as a CSV file (see [Export campaign statistics](https://docs.pushwoosh.com/product/customer-journey/journeys-list/#export-campaign-statistics)) * **Create campaign:** [add a new campaign](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project/) ![Campaigns list with search, status filters, category filter, and campaign cards showing name, status, type, and metrics](https://docs.pushwoosh.com/customer-journey-journeys-list-1.webp) ### Campaign statuses Use status filters to quickly find campaigns by lifecycle state: * **Draft:** campaigns you have created but not launched yet. You can edit them and launch from the canvas. * **Active:** campaigns that are currently running and can send messages. You can edit them, pause for editing, clone, or stop. If the campaign has an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#when-entry-closes) with its entry window closed, the badge reads **ACTIVE · ENTRY CLOSED** instead of just **ACTIVE**. * **Paused:** active campaigns you temporarily paused from the canvas using **Pause to edit**. You can safely edit settings and content while the campaign is paused, or stop it from the campaigns list. * **Inactive:** campaigns you stopped using **Stop campaign**. Restore them to resume editing, or clone them if you want to run a similar campaign again. * **Archived:** campaigns you moved out of the main list from the **Inactive** state to keep the list tidy. This is irreversible — you can view archived campaigns under the **Archived** filter and clone them, but not restore them. For more details on what you can do in each state, see [Actions with journeys](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#actions-with-journeys). ## Customer Journey card Each campaign has its own dedicated Customer Journey card. This card acts as a one-stop shop for all relevant information, including: * **Campaign overview** providing key details such as the journey’s name, status, type, and other relevant information. * **Campaign performance metrics** to gauge the campaign’s effectiveness. * **Detailed message statistics** to dive deeper into the performance of each message within the campaign. ### Campaign overview Each campaign card shows: * **Campaign name** at the top left * **Status:** lifecycle state of the campaign (e.g. INACTIVE, DRAFT, ACTIVE, PAUSED, ARCHIVED, or ACTIVE · ENTRY CLOSED). See [Campaign statuses](https://docs.pushwoosh.com/product/customer-journey/journeys-list/#campaign-statuses) for what each means. * **Options menu** next to the status. Click it to open actions such as **Edit**, **Clone**, and **Stop campaign** (or **Restore** for an inactive campaign) * **Last modified:** date and time and who made the change * **Campaign type:** [Trigger based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/), [Audience based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), or [Mixed](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) * **Category:** category name (e.g. DEFAULT, ONBOARDING) with a dropdown to change it * **Message count:** the number of messages in the campaign ![Campaign card with name, status pill, options menu, last modified, type, category with dropdown, message count, and metrics](https://docs.pushwoosh.com/customer-journey-journeys-list-2.webp) ### Campaign performance metrics Each card shows three metrics (as **TOTAL REACH**, **REACHED GOALS**, **AVG. CTR**). For draft or new campaigns they may show as `--` until there is data: * **Total reach:** number of unique users who entered or were reached in the journey * **Reached goals:** number of times journey goals were achieved * **Avg. CTR:** average click-through rate for all messages in the journey (any channel) ![Campaign performance metrics showing unique users, journey goals achieved, and average click-through rate statistics](https://docs.pushwoosh.com/customer-journey-journeys-list-3.webp) ### Detailed message statistics To see detailed statistics for each message in a campaign, click the **statistics** (bar chart) icon with the dropdown next to that campaign in the list. For more on journey metrics, see [Customer Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/). ![Detailed message statistics displaying message name, sends, opens, open rate, clicks, and click rate for each campaign](https://docs.pushwoosh.com/customer-journey-journeys-list-4.webp) This will reveal a detailed breakdown of each message’s performance, including: * The name of each message. * How many times the message was sent or shown to users. * The number of times the message was opened by recipients. * The message open rate: the percentage of opens relative to the number of times the message was sent or shown. * Clicks: the total number of times users engaged with the message content by clicking a link. * Click rate: the percentage of users who took an action within the message relative to the total number who received it. To ensure you’re viewing the latest performance data, click the **Update Data** link. This will refresh the statistics and provide the most up-to-date insights into your message effectiveness. ## Manage your Customer Journeys ### Journey categories With Pushwoosh, you can effortlessly organize your journeys by adding custom categories to the campaigns list. You have the flexibility to create up to 10 personalized categories tailored to your needs. ### Create Customer Journey categories To create customer journey categories, follow these steps: 1. Navigate to the top of the category list and click **All categories**. 2. Select **Manage categories** from the drop-down list. ![Category dropdown menu showing All categories option and Manage categories selection for organizing customer journeys](https://docs.pushwoosh.com/customer-journey-journeys-list-5.webp) 3. In the window that appears, click **Add category** and create a name for your category that is informative and within 30 characters. ![Add category dialog window with input field for creating new custom journey categories with 30 character limit](https://docs.pushwoosh.com/customer-journey-journeys-list-6.webp) 4. You can also edit and remove your categories by selecting **Manage categories**. Note Please note that when you delete a category, all the journeys within it will be automatically moved to the **Default** category. ### Assign Journeys to categories When you create a journey, it automatically lands in the **Default** category. To assign it to a different category, click on the dropdown menu labeled **Default** below the journey’s name. Then, choose the desired category from the list that appears. If you need to create a new category, select **Manage categories**. ![Category assignment dropdown showing Default category selection and Manage categories option for journey organization](https://docs.pushwoosh.com/customer-journey-journeys-list-7.webp) ### Searching and filtering your customer journeys * **Search:** use the “Search by name…” bar at the top to find campaigns by name. * **Status:** use the tabs to show All, Active, Inactive, Draft, Paused, or Archived campaigns. * **Category:** use the “Any category” dropdown to filter by category. * **Show filters:** click to open more filter options. ### Journey actions To manage a campaign from the list, click the **three vertical dots** (options menu) next to the campaign status. The menu includes: * **Edit:** open the campaign canvas to change settings and content * **Rename:** change the campaign name * **Clone:** create a copy of the campaign with the same settings so you can change the copy without affecting the original * **Stop campaign:** end an **Active** or **Paused** campaign and mark it **Inactive**. Removes users currently in the campaign, but you can **Restore** the campaign later to resume editing. * **Restore:** available for an **Inactive** campaign. Returns it to **Paused** so you can edit and launch it again. Users removed when the campaign was stopped don’t come back. * **Archive:** available for an **Inactive** campaign. Moves it out of the main list. This action is irreversible — an archived campaign cannot be restored, only cloned. Other actions (e.g. **View**) may also appear in the menu depending on the campaign state. Delete campaign Deleting a campaign is irreversible. Use it with caution. ![Journey actions menu with View, Deactivate, and Clone options](https://docs.pushwoosh.com/customer-journey-journeys-list-8.webp) ### Archive or delete several campaigns at once Use **Bulk actions** to archive or delete up to 100 campaigns in a single action. Each action applies only to campaigns in a specific [status](https://docs.pushwoosh.com/product/customer-journey/journeys-list/#campaign-statuses): * **Archive:** only **Inactive** campaigns. Stop an **Active** or **Paused** campaign first. * **Delete:** only **Draft** campaigns that were never launched. Their content is deleted with them. To archive or delete campaigns: 1. Click **Bulk actions** next to **Export statistics** at the top of the Campaigns page. 2. Select **Archive…** or **Delete…**. 3. Check the boxes on the campaign cards. To select every campaign on the current page, check **Select all on page**. 4. Click **Archive N campaigns** or **Delete N campaigns**. 5. In the confirmation window, check **I understand this can’t be undone**. 6. Click **Archive N campaigns** or **Delete N campaigns** to confirm. N is the number of selected campaigns the action applies to. Selected campaigns in other statuses stay as they are, and the confirmation window lists them separately. If more than 100 selected campaigns are eligible, Pushwoosh shows **Select up to 100 campaigns at a time** and doesn’t open the confirmation window. Deselect some campaigns and click the button again. To exit without making changes, click **Cancel** at the top of the page. ### Create a new customer journey To create a new customer journey, click the **Create campaign** button at the top of the list. ![Create campaign button interface with options to build journey from scratch or use pre-built journey templates](https://docs.pushwoosh.com/customer-journey-journeys-list-9.webp) Here, you have the flexibility to: * Build a journey from scratch and tailor it to your specific scenarios. [Learn more](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project) * Leverage pre-built journey templates for common customer journey scenarios. [Learn more](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project#build-a-customer-journey-with-ready-made-journey-templates) ### Export campaign statistics Click **Export statistics** at the top of the Campaigns page. In the **Export campaign statistics** dialog, choose a date range and click **Export** to download campaign data in CSV format. You can use the file in spreadsheets or third-party tools to analyze and optimize future campaigns. **What you get in the CSV** * **Campaign info:** Title, Status, Created, Category, Type, UUID * **Reach and users:** Total reach, Unique users, Users inside, Drop-off * **Overall:** Total sends, Avg CTR, Goals * **Per channel (push, email, SMS, WhatsApp, in-app):** sends, deliveries/opens, CTR, and channel-specific metrics (e.g. email clicks and unsubscribes, in-app shows and skips) --- # Pushwoosh Customer Journey overview **URL:** https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/ Create automated omnichannel campaigns with Pushwoosh Customer Journey. Track user behavior and send personalized messages --- [Youtube Video: Create automated omnichannel campaigns with Pushwoosh Customer Journey](https://youtube.com/watch?v=5-bZSSKpFd8) Pushwoosh Customer Journey is a single-interface marketer tool to create cross-channel customer experiences. It allows you to track user behavior and send relevant behavior-based messages at every stage of their lifecycle. Benefit from using Pushwoosh Customer Journey: * **Set up your campaigns faster:** Create chains of push notifications & emails launched by user’s actions or specific conditions based on user’s properties. * **Keep your campaigns organized:** Cross-channel messaging displayed in one place. See the whole picture and coordinate your messages with Pushwoosh Customer Journey. * **Optimize campaign performance:** Track the results of your campaigns and fine-tune your messages to improve their efficiency, increase customer retention and reach your goals faster. The visual campaign builder makes it easier to plan your communications. Pushwoosh Customer Journey dashboard consists of [journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements) and the canvas where you visualize your campaigns. [](https://docs.pushwoosh.com/customer-journey-pushwoosh-journey-overview-1.webm "Customer Journey dashboard with visual campaign builder and journey elements for automated marketing campaigns") Communicate with customers throughout their journey, from prospects to brand advocates, and improve your business results while efficiently achieving your KPIs. With Pushwoosh Customer Journey, creating your marketing campaigns is simple, visualized, and providing all the significant touchpoints are covered. ## Pushwoosh Customer Journey guides Before starting, ensure everything is set up by following the [Prepare your project](https://docs.pushwoosh.com/product/customer-journey/getting-started/) guide. You can also [create a test journey](https://docs.pushwoosh.com/product/customer-journey/how-to-try-it-out/) and explore all the features of Pushwoosh Customer Journey, including elements, settings, editing options, and statistics. [Prepare your project ](https://docs.pushwoosh.com/product/customer-journey/getting-started/) [Create a test journey ](https://docs.pushwoosh.com/product/customer-journey/how-to-try-it-out/) [Journey elements ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) [Journey settings ](https://docs.pushwoosh.com/product/customer-journey/journey-settings/) [Journey editing ](https://docs.pushwoosh.com/product/customer-journey/journey-editing/) [Journey history and versions ](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/) [Canvas navigation and controls ](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) [Journey statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/) You can also read our guides on creating campaigns for every stage of the user lifecycle: * [Onboarding](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) * [Activation](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/) * [Engagement](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh/) * [Retention](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/) * [Re-engagement](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh/) --- # Pushwoosh glossary **URL:** https://docs.pushwoosh.com/product/first-steps/pushwoosh-glossary/ This glossary contains the most common terms used in Pushwoosh. Understanding these terms will help you get the most out of the platform. --- Missing a definition? Just [send us a message](https://help.pushwoosh.com/hc/en-us/requests/new) and we’ll add it to the list. ## A ### A/B/n split A Customer Journey element that lets you test up to four message variations (branches) and compare performance. You can choose the winning branch manually or have it selected automatically based on a conversion goal once results are statistically significant. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split) ### Aggregated messages A Statistics section where you group one-time push notifications and one-time emails into campaigns. Combined delivery and engagement metrics are shown in one view so you can track performance of a set of related messages over time. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages) ### API-based entry An entry block in the Customer Journey tool that allows you to launch a journey when specific events occur outside the app (such as item returns to stock). These events are typically tracked in your internal database. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry) ### Apple Wallet passes Digital cards, such as loyalty cards, coupons, event tickets, boarding passes, and store cards, that users keep in the Wallet app on iPhone and Apple Watch. You design and update passes in the Control Panel, and Pushwoosh pushes changes to every device that added the pass. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) ### Application A mobile app or website that is registered with the Pushwoosh platform. A registered Application enables you to send push notifications, in-app messages, emails, and SMS messages to your users through Pushwoosh. Each Application has its unique settings, segments, and user data. ### Application code A unique identifier associated with your mobile application or website within the Pushwoosh platform. ### Audience Users who are intended to receive a particular push notification, email, or in-app message that you send through Pushwoosh. In marketing terms, a target segment. ### Audience-based entry An entry block in the Customer Journey Builder tool that automatically initiates journeys by enrolling users who meet specific criteria into pre-configured sequences of interactions. This approach leverages established segments of your app’s user base, ensuring relevant journeys are triggered only for the most appropriate audience. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry) ### Audience sync A Customer Journey element that adds or removes users from a Meta Ads custom audience when they reach this step, for ad targeting or suppression. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/audience-sync) ### Average monetary values (AMV) The average spend for each user in the segment, whether that’s money, time, or any other countable metric. ## B ### Behavior-based messaging Messages targeted to specific segments of your audience based on user behavior and preferences. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/targeted-push) ## C ### Cancel events Conditions that remove users from a customer journey as soon as they are met, without counting as conversions. Use them for actions that should stop further messaging but are not campaign success (e.g. unsubscribe, disabling push). Set in Journey settings. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#cancel-events-remove-users-from-a-journey-without-counting-a-conversion) ### Channel A specific communication medium through which you can deliver messages to your users. Pushwoosh supports various channels, including push notifications, in-app messages, emails, SMS, and WhatsApp. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/) ### Cloud pages Hosted landing pages and forms built in a visual editor for lead capture, quizzes, and click-through campaigns. Form submissions can trigger Customer Journeys. [Learn more](https://docs.pushwoosh.com/product/content/cloud-pages) ### Condition split A Customer Journey element that branches a flow into up to ten paths based on whether users match a Segment, Tag value, or Event attribute. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split) ### Control groups Named holdouts, a Default group plus any number of Additional groups, that exclude a fixed percentage of your project users from marketing messages (Push, Email, In-app, SMS, WhatsApp) to measure campaign effectiveness. Control group users still receive transactional messages. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups) ### Control panel An admin panel for using Pushwoosh features in the user interface. It allows you to create and send notifications, track user engagement, customize settings, and optimize your campaigns for success. ### Conversion Any target action a user takes, like a purchase or sign-up, that aligns with your campaign goals. ### Conversion funnel A visual representation of the user journey that allows tracking conversion rates at each stage after a push notification. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#campaigns-distribution) ### Conversion goal A specific action or outcome that marks the successful completion of a customer journey. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals) ### Conversion tracking A feature that presents data about how often users perform desired actions after receiving a push. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#custom-events-after-a-tap-on-this-push) ### Custom rich media A deeply customizable HTML page containing images, videos, buttons, and other media content. [Learn more](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax) ### Custom tags Tags that serve as a way to store user attributes and can be created based on the segmentation logic or targeting pattern that is appropriate for your unique business needs.\ [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags) ### Customer journey A campaign that uses communication to guide users through a series of interactions and influence their behavior toward a desired outcome. [Learn more](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project) ### Customer journey builder A tool for creating automated cross-channel campaigns. It allows you to keep track of user behavior and send relevant behavior-triggered and scheduled messages to your customers at every stage of their lifecycle. [Learn more](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) ## D ### Deep link A type of link that allows you to open a specified section and resources of your application once a user taps on a push notification, in-app, or email. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) ### Default tags Some non-personal data about the end user’s device that is collected by Pushwoosh SDK by default (no integration required). This allows addressing some basic segmentation and lifecycle scenarios (e.g., Last Application open date to launch reactivation campaigns, Push Alerts Enabled (true/false) for opt-out recovery campaigns, App version — if you want users to update from the older versions) [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#default-tags) ### Dynamic content Placeholders within push content (body, title) that are automatically filled with tag values for content personalization. [Learn more](https://docs.pushwoosh.com/product/personalization/dynamic-content) ## E ### Email content An email layout that contains textual and multimedia elements, as well as email settings, which you can use to create and launch your email campaigns quickly and easily. [Learn more](https://docs.pushwoosh.com/product/content/email-content) ### Email editors A [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor), which allows you to design emails easily without coding or design skills, and an [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor) for those proficient in HTML who prefer to write their own code. Both tools offer a range of content elements and layouts to help you quickly and effectively create and launch email campaigns. ### Entry schedule An automated start of a journey, either at regular intervals or pre-defined dates and times. This ensures your journeys are delivered at the optimal moment for each user, based on their time zone and any relevant segment updates. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#entry-schedule) ### Event Any user activity within the app, website, or backend system. They can be used in segmentation and as triggers for behavior-based messaging. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events) ### Exit The point where a Customer Journey branch ends. Add it once all communications in a flow are defined to complete the branch. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit) ## G ### Global frequency capping This is a feature that limits the number of messages a user receives within a specific timeframe. Once the message limit is reached for a channel, no further messages will be sent until the period resets. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) ### Google Wallet passes Digital cards, such as loyalty cards, offers, gift cards, event tickets, boarding passes, and transit tickets, that users keep in the Google Wallet app. You design and update passes in the Control Panel, and changes sync automatically to every saved device. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) ## H ### High-priority in-app A setting in Pushwoosh that ensures that in-app messages are displayed promptly, even if the Pushwoosh SDK is still downloading the message content. When this setting is enabled, the SDK will temporarily block the app’s UI for a few seconds while the message is being downloaded, ensuring that users see the message as soon as possible. ### High-speed delivery segments Optimized user segments that are pre-calculated and updated every 10 minutes to ensure rapid message delivery. This feature eliminates the need for real-time segment recalculation when sending messages, minimizing latency and ensuring that messages reach the target audience promptly. This optimization is especially useful for large databases (e.g., those exceeding 1 million) and time-sensitive push notifications. ### HWID (or hardware ID) A key identifier assigned to the device by Pushwoosh SDK. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers#hwid) ## I ### In-app messages Customizable HTML pages that can be displayed to app users within the app. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps) ### Instant in-apps A way to show an in-app message as soon as a specific event occurs, without building a full customer journey. You choose the trigger event and the in-app content. The message is displayed immediately when the event fires. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) ## K ### Kakao (KakaoTalk) A messaging platform widely used in South Korea. You can send KakaoTalk messages through Customer Journeys or the API using pre-approved message templates. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/kakao) ## L ### LINE A messaging platform used to reach users through Customer Journeys, the API, or direct 1:1 conversations, using predefined templates or free-form text. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/line) ### Liquid templates A syntax that allows populating dynamic placeholders in the content templates based on logical expressions. E.g. ```plaintext `{% if Language == 'fr' %}` Salut! `{% else %}` Hello! `{% endif %}` ``` [Learn more](https://docs.pushwoosh.com/product/personalization/liquid-templates#_top) ## M ### Marketing message A message type for Push, Email, and In-app that respects user subscription preferences, opt-outs, and frequency capping. Marketing messages are not sent to users in the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). Use for promotions, newsletters, and other non-essential communications. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional) ### Message history A section that shows the detailed statistics on each message sent. You can easily find information on a specific message using filters by campaign name, text, notification ID, or notification code. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history) ### Message inbox (also known as message center or app inbox) An extra communication channel allowing you to save push notification messages of your choice in a dedicated section within your app. There, the messages will stay available for users to read and take action during the time you specify. Also, you can deliver messages to the inbox even if a user has disabled push notifications. This functionality provides users with the flexibility to consider your special offers at their convenience and, thus, increases chances for higher engagement and conversion rates. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-inbox) ### Mobile push notification A short message sent from a mobile app instantly, on a specified schedule, or upon a user-triggered event. It can only be delivered to opted-in users – then, it will appear on their mobile device’s screen. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications) ### Multi-Armed Bandit A Customer Journey channel element that sends one of several push presets per user and continuously shifts traffic toward the variant with the highest open or conversion rate. Unlike [A/B/n split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/), it does not lock in a single winner or split users into separate journey branches. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/multi-armed-bandit/) ### Multi-language messaging A feature that allows delivering the same message with different content depending on the device’s locale settings. For push notifications, you can add multiple language versions in the push preset; Pushwoosh suggests popular languages from your database. [Learn more](https://docs.pushwoosh.com/product/content/push-presets#define-the-languages-you-want-to-use-in-your-push) ### Multi-login accounts A feature within Pushwoosh that enables multiple team members to access and manage a single Pushwoosh account simultaneously. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts) ## N ### No-code in-app editor A drag-and-drop tool that empowers users with no coding skills to create and customize in-app message content. The tool features various options for copy editing and visual design, such as built-in stock photo search and AI image generation. It also supports personalization methods, ensuring that messages resonate with target audiences. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) ## O ### One-time messaging Sending a single push notification or email to an audience without building a customer journey. You choose the content, audience, and schedule (immediate or later). Use it for standalone campaigns instead of automated multi-step flows. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) ## P ### Passkeys A passwordless way to sign in to Pushwoosh. Instead of a password, you confirm access with Touch ID, Face ID, Windows Hello, or a device PIN. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/passkeys) ### Pre-launch check A check Customer Journey Builder runs when you click **Launch campaign**. If the journey isn’t ready, a **Validation** panel lists the issues to fix. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) ### Push preset A reusable push notification template stored under Content > Push presets. You define the title, body, media, and actions once, then use the preset in one-time pushes or in customer journey steps. Supports personalization (e.g. dynamic content, Liquid templates). [Learn more](https://docs.pushwoosh.com/product/content/push-presets/) ### Project A central organizational unit that encompasses all the settings and configurations related to a specific mobile or web application. It’s a workspace for managing various aspects of your app’s communication and engagement strategy with users. ### Project overview report A dashboard where you can analyze your marketing campaigns’ performance with comprehensive stats for your entire project. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) ### Push-enabled device A device with a valid push token that can receive notifications. On mobile devices, users can ban the display (rendering) of push notifications on the lock screen in the app-specific OS settings, however, such devices are still considered push-enabled because the token is preserved, and they can receive silent notifications (sent to the app inbox, containing custom data, etc.) [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices#push-enabled-devices) ### Push token (also known as device push token) A unique key for the app-device combination issued by the Apple or Google push notification gateways. It allows gateways and push notification providers to route messages and ensure the notification is delivered to the unique app-device combination for which it is intended. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers#push-token) ### Pushwoosh An omnichannel customer engagement platform allowing businesses to communicate with their end customers through mobile and web channels: mobile and web push notifications, in-app messages, emails, SMS, and WhatsApp. In addition to messaging, Pushwoosh serves as a customer data platform, providing features for audience segmentation and performance analytics. [Learn more](https://docs.pushwoosh.com/product/first-steps/what-is-pushwoosh) ### Pushwoosh AI composer An AI tool that generates engaging, high-quality copy for push notifications based on the keywords you specify. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer) ### Pushwoosh API A set of publicly accessible methods to communicate to Pushwoosh to send messages, track users’ behaviors, gain statistics, etc. [Learn more](https://docs.pushwoosh.com/developer/api-reference/) ## R ### Reachability check A Customer Journey element that routes a user to the first reachable channel from an ordered list you choose (push, email, SMS, WhatsApp, LINE, in-app, or wallet pass), with a fallback branch for everyone else. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check) ### Reachable by in-app All devices that meet the Segment conditions, including those with push notifications disabled. You can show in-apps on all these devices, while some of them may be unavailable for push notifications. ### Reachable by push Devices with push notifications enabled, including those that can only receive silent pushes. These devices are also available for displaying in-app messages. ### Recipients Users who have given permission to receive messages (push or email) and whom you can reach with your communication. ### RFM segmentation / (recency, frequency, monetary) segmentation An analytics tool in Pushwoosh that allows you to segment your audience based on the Recency, Frequency, and Monetary criteria. The values are assigned upon user behavior tracked as triggered events. The tool can also be used for RF (non-monetary) segmentation, which is relevant for subscription-based or hybrid apps monitoring user engagement and retention metrics. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation) ### RFM tags A set of tags that store and categorize user data based on their Recency, Frequency, and Monetary value (RFM). These tags are used to create targeted segments of users for personalized messaging campaigns. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation#rfm-tags) ## S ### Segment A cohort of app or web users complying with your specified conditions – assigned tags’ values (user attributes) and/or triggered events (user behavior). [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation) ### Send rate limit A feature that allows you to restrain the push-sending speed. That means Pushwoosh will send no more than the specified number of messages in a second. Please note the available values are from 100 to 1000 pushes per second. ### Silence period A feature that allows defining a do-not-disturb period during which no notifications or emails are sent to users. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings#silence-period) ### Silent push notification A push that is not visible or audible to the user and allows passing some data to the app, updating the app in the background, or getting new data from your server without having users informed. Silent push notifications are delivered to users’ devices with no alert, sound, or icon badge. When a silent push arrives, the app is woken up in the background. Users are not notified of any alerts and don’t see any push content. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications) ### SMS A text-message channel for reaching customers who aren’t subscribed to push or email, sending transactional notifications, and delivering marketing offers. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms) ### Subscription preference center A feature that lets email subscribers choose which types of emails they want to receive (e.g. newsletter, promotions, product updates). Users can opt out of specific categories instead of unsubscribing from all emails. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) ## T ### Tag A way to store information about users based on their characteristics, preferences, and behavior. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) ### Tag operator A logical operator that defines the relationship between the Tag and its values for segmentation purposes. ### Tag value A specific piece of information stored within a tag that helps make push campaigns more targeted or personalized. ### Telegram A messaging channel connected to Pushwoosh via a Telegram bot (set up through BotFather), used to automate onboarding, reminders, and follow-ups in Customer Journey Builder. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/telegram) ### Time delay An element of a Customer Journey that pauses the user’s progress for a specified duration before proceeding to the next step. This delay can be set to a fixed period, a specific time, a particular date and day of the week, or dynamically determined based on a user tag value or event attribute. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) ### Transactional message A message type for Push, Email, and In-app that is sent to all users regardless of subscription status. Transactional messages bypass opt-outs and frequency capping and are delivered to users in the control group. Use for password resets, order confirmations, and other essential communications. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional) ### Trigger-based entry An entry block inside the Customer Journey Builder tool. It contains an event that a user should trigger to start the journey. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) ### Two-factor authentication (2FA) An extra layer of account security that requires a second verification step at login (an email code or an authenticator app) in addition to your password. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/two-factor-authentication) ## U ### Update user profile A Customer Journey element that assigns Tag values to users as they move through a journey, either manually, from Event attributes, or from a Webhook response. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile) ### User explorer A tool that provides in-depth insights into individual user or device data. It enables you to delve into various aspects of user behavior, including device usage, message interactions, and overall engagement patterns. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer) ### User ID As opposed to the HWID (Hardware ID), a user ID defines the user of the device. By default, it is equal to HWID but can be overridden with the [`/registerUser`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser) API (often called when a user logs into the app). Multiple devices (HWIDs) can be united under a single User ID. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids) ## V ### Viber A messaging platform you can use to send messages through Customer Journeys, using either a Rakuten-approved template or freeform text content. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/viber) ### Voucher pool A collection of unique promo codes (vouchers) that share a name and optional expiration date. You upload codes via CSV, then use the pool in Customer Journey to insert one code per user into push, email, SMS, and other messages. Useful for discounts, referrals, and one-time offers. [Learn more](https://docs.pushwoosh.com/product/content/vouchers/) ## W ### Wait for Trigger A Customer Journey element that branches users based on whether they trigger one or more specific events within a set timeframe (up to 90 days), with a fallback branch for users who don’t. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger) ### Web popups On-site messages that appear over your web pages, used for promotions, onboarding hints, and other on-site campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups) ### Web push notifications Clickable push messages you receive in your web browser or mobile browser. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/) ### Webhook A Customer Journey element that sends and receives journey data to and from external services, such as CRMs, analytics tools, or marketing platforms, via HTTP requests. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook) ### WhatsApp (Messages) A messaging channel for reaching customers who ignore other channels, such as push notifications, in-app messages, or emails. Supports both outbound campaigns and inbound-triggered campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages) --- # Navigating Pushwoosh **URL:** https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/ Learn how to navigate the Pushwoosh interface, from the Projects page to the project workspace sidebar and each section within it. --- This guide helps you navigate the Pushwoosh interface and find the tools you need to create campaigns, manage your audience, organize content, track performance, and configure your project. Learn where to go to accomplish specific tasks and understand how each section helps you engage users across multiple messaging channels. ## Projects page When you log into Pushwoosh, you start on the **Projects page**. This is your central hub for managing all your applications. Each application is treated as a separate project in Pushwoosh. Use the search field above the table to find a project by name or app code. Each row in the projects table represents a project. Statistics columns cover the last 30 days. * **Name**: the project name and, below it, the unique Pushwoosh app code. * **Audience now**: contacts who can receive messages right now, meaning devices with a push token, email addresses, and phone numbers. Below it is the total number of contacts in the project. * **New contacts**: new subscribers, meaning contacts who got a push token, email address, or phone number. Below it is the number of new registrations: every new device or contact added through the SDK or API, including app installs without push permission. * **Sent**: messages sent across all channels. * **Opened messages**: messages opened, with each contact counted once per message. Push taps, email opens and link clicks, and SMS and WhatsApp messages marked as read all count as opens. * **Platforms**: the messaging channels connected to the project. If no platform is connected yet, the column shows **Set up** instead, which opens [platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) for the project. ![Projects page with the Name, Audience now, New contacts, Sent, Opened messages, and Platforms columns, and the Set up button for a project with no connected platforms](https://docs.pushwoosh.com/pushwoosh-interface-overview-1.webp) To sort the list, click **Name** or **Audience now**. The first click on **Audience now** puts the projects with the largest audience at the top. Hover over a project row to open **One-time messages**, **Customer Journey Builder**, or **Project overview** for that project. ![Hovered project row with the One-time messages, Customer Journey Builder, and Project overview buttons highlighted](https://docs.pushwoosh.com/pushwoosh-interface-overview-18.webp) Click **Create project** to create a new project. The button is shown only if your account role allows you to modify projects. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/) ### All projects report Click **All projects report** next to **Create project** to see combined performance across all projects your account role can open. The list isn’t configurable. Set the date range with the picker above the table. It defaults to the last 30 days. The table has one row per project and a **Total** row at the top. Its four columns match the Projects page: * **New subscribers** and **New registrations**: the two numbers in the **New contacts** column. * **Sent** and **Opened messages**: the columns with the same names. ![All projects report with a Total row and per-project New subscribers, New registrations, Sent, and Opened messages columns](https://docs.pushwoosh.com/pushwoosh-interface-overview-17.webp) Note Each column counts events that occurred during the selected period, not your current audience size. This differs from **Audience now** in [the projects table](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#projects-page), which is a snapshot of contacts who can receive messages today. The **Total** row adds up the project rows. For **New subscribers** and **New registrations**, this means a contact registered in two of your projects is counted twice. ### Notification center (top right corner) Click the bell icon in the top right corner to see alerts about your account, such as warnings and critical issues. Click **View** to see details or **Expand** to read the full message. ![Notification center in the top right corner showing critical and warning alerts](https://docs.pushwoosh.com/pushwoosh-interface-overview-11.webp) ### Product updates Click the megaphone icon next to the bell to see new features and improvements released in Pushwoosh. ![Product updates panel showing unread count, update cards with images and dates, and NEW badges](https://docs.pushwoosh.com/pushwoosh-interface-overview-16.webp) After selecting a project, you are taken into the project’s workspace. Here, you can create and manage messaging campaigns across different channels, view statistics, configure platforms, and more. ## Project workspace overview The Pushwoosh interface is organized into logical sections accessible via the left sidebar. Each section serves a specific purpose in your campaign management workflow. | Section | Description & functionality | | ----------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **[Current project](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#current-project)** | View your current project information and switch between projects. | | **Create button** | Quick access to create new campaigns, messages, segments, or import users. | | **[Project overview](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#project-overview)** | View a summary dashboard with recent activity and key performance metrics. | | **[AI Assistant](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#manymoney-ai)** | Get help and guidance for any task from ManyMoney AI. | | **[Campaigns](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#campaigns)** | View, create, and manage campaigns for messaging and automation. | | **[Audience](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#audience)** | Manage user data, view subscribers, and create audience segments. | | **[Content](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#content)** | Create, organize, and manage message templates and content for all channels. | | **[Statistics](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#statistics)** | Track campaign and messaging performance, and view analytics dashboards and reports. | | **[Settings](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#settings)** | Set up your project, including platform configuration, message delivery settings, third-party integrations, control group, API access, deep links, test devices, and application permissions. | | **Documentation** | Access user guides and developer documentation. | | **[My account](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#my-account)** | Access account settings, billing, user management, referral program, and log out. | ### Current project At the top of the sidebar, you’ll find information about your current project. This area displays: * **Project name** and **application code** (formatted as XXXXX-XXXXX, where each X is an uppercase letter or digit) * **Application detail**: Shows the number of enabled devices with percentage and the total number of devices in the application. * **Manage Projects** link to access project management settings. Click it to switch between different projects (apps) in your Pushwoosh account. ![Project information in sidebar: project name, ID, application statistics, and Manage Projects link](https://docs.pushwoosh.com/pushwoosh-interface-overview-2.webp) ### Project overview The **Project overview** is the main dashboard you see after opening a project. It shows key audience metrics and gives you quick access to core actions. For a deeper breakdown of active users, retention, and push and email delivery performance in one place, see [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/). The dashboard includes: ##### Quick action buttons Three buttons for fast access to core tools: [**Create campaign**](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), [**Create content**](https://docs.pushwoosh.com/product/content/), and [**Build segment**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/). ![Project overview quick action buttons: Create campaign, Create content, and Build segment](https://docs.pushwoosh.com/pushwoosh-interface-overview-13.webp) ##### Summary statistics The **Total devices** widget displays the total number of devices registered in your project, broken down by platform. ![Total devices widget in Project overview showing registered device count by platform](https://docs.pushwoosh.com/pushwoosh-interface-overview-14.webp) The **Unique users** widget shows how many unique users opened the app. Each user is counted once regardless of how many devices they use. Each chart also shows the change compared to the previous period. The widget includes two interactive charts: * **MAU (Monthly Active Users)**: The number of unique users that opened the app in the 30 days up to each day on the chart. * **DAU (Daily Active Users)**: The number of unique users that opened the app on a certain day. ![Project overview in main workspace: dashboard with project header, audience information, create message section, and quick action buttons](https://docs.pushwoosh.com/pushwoosh-interface-overview-3.webp) ##### Frequency capping The **Frequency capping** block shows how [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) affected message delivery across your project over the last 30 days. It displays two metrics: * **Suppressions**: the number of times frequency capping prevented a message from being sent over the last 30 days. These sends are not counted in Sent or Delivered. * **Affected users**: the number of unique users affected by frequency capping over the last 30 days. A single user can be suppressed multiple times within the period, so Suppressions is usually higher than Affected users. ![Frequency capping block in Project overview showing suppressions and affected users for the last 30 days](https://docs.pushwoosh.com/pushwoosh-interface-overview-15.webp) [Learn more about frequency capping ](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) ##### Connect more platforms Click **+ CONNECT MORE PLATFORMS** to expand your messaging capabilities by adding additional communication channels. ##### More resources Use the **More resources** section to find shortcuts to documentation, the Help Center, webinars, pricing, and other useful materials. Below these links, **Product updates** opens the same list of new features and improvements as the [megaphone icon](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#product-updates) in the top bar. The item is hidden when there are no updates to show. ### ManyMoney AI Pushwoosh’s AI-powered marketer that can help with almost any task in the platform. Access ManyMoney AI from the navigation sidebar or via the floating button in the bottom-right corner of the screen. What ManyMoney AI can do: * Answer questions about Pushwoosh features and documentation * Create and configure applications * Build and optimize marketing campaigns * Manage user data and segmentation * Generate content and messaging * Set up customer journeys * Provide analytics and reporting * Handle advanced configurations and much more ![ManyMoney AI: AI assistant interface in sidebar or floating button](https://docs.pushwoosh.com/pushwoosh-interface-overview-4.webp) [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) ### Campaigns The **Campaigns** section contains all your campaign management tools: * **Customer Journey Builder**: Design and automate cross-channel customer experiences with an intuitive visual canvas. Easily view and manage all your journeys, along with detailed information for each. [Learn more](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) * **One-time messages**: Send single push notifications or emails to your audience without building a full customer journey, and schedule them for immediate or later delivery. From here you can also review the [history of all sent messages](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history) and open detailed statistics for any of them. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push) * **Instant in-apps**: Display in-app messages immediately when specific events occur, without building a full journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) * **Web campaigns**: Show targeted overlay messages to visitors on your website and track impressions and conversions. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) * **Geo campaigns**: Trigger push notifications by location using geozones, clusters, and cooldowns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/) * **Wallet Passes**: Design, distribute, and update Apple Wallet and Google Wallet passes. Updates sync to every device that saved the pass. [Apple Wallet](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) · [Google Wallet](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) * **Messenger chats**: Manage conversations across messenger channels such as SMS, WhatsApp, LINE, and others. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/#messenger-chats-and-related-channels) ![Campaigns section in sidebar: expandable menu with Customer Journey Builder, One-time messages, Instant in-apps, Web popups, Geo campaigns, Apple Wallet, and Messenger chats](https://docs.pushwoosh.com/pushwoosh-interface-overview-5.webp) ### Audience The **Audience** section lets you manage user data, create and view segments, track and manage events, and organize your users for targeted messaging. Use it to build dynamic user groups, store and view user attributes, import user data, and explore individual user profiles. Here you can manage: * **Segments**: Create and manage dynamic user groups based on tags, events, or behavior patterns. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) * **Tags**: Store and view user attributes like language, location, subscription status, or custom properties. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) * **Events**: Track user behaviors and actions within your app or website, and view all tracked events. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) * **User Explorer**: View individual user profiles, message history, and engagement data. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer) * **Import CSV**: Create segments from CSV files, import email/SMS contacts, and update tag values. * **RFM Segmentation**: Use Recency, Frequency, and Monetary segmentation to classify users based on engagement. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation) * **Subscription forms**: Create embedded forms that add subscribers and tags to your audience, with styling and double opt-in options. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) ![Audience section in sidebar: expandable menu with Segments, Tags, Events, User Explorer, Import CSV, RFM Segmentation, and Subscription forms](https://docs.pushwoosh.com/pushwoosh-interface-overview-6.webp) ### Content The **Content** section stores all your content and lets you create, view, organize, and manage content for all channels. Use it to access existing content, generate new content, organize it by category, preview how it appears across devices, and handle versions or translations. Within the Content section, you can access and manage the following types of content: * **Push presets**: View, create, and manage reusable push notification templates with customizable text, images, and actions. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/) * **Email content**: View, build, and edit HTML emails using templates or the Drag & drop editor. [Learn more](https://docs.pushwoosh.com/product/content/email-content/) * **In-apps**: View and design interactive messages that appear inside your app. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/) * **Messenger presets**: View and create SMS, LINE, and Kakao presets and rich messages. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/) * **Vouchers**: View, generate, and manage promo code pools for personalized offers. [Learn more](https://docs.pushwoosh.com/product/content/vouchers) * **Web content**: Design Web popup layouts from starters or a blank template, then attach them to website Web popup campaigns. [Learn more](https://docs.pushwoosh.com/product/content/web-popup-content/) * **Cloud Pages**: Create Cloud Pages with forms to capture leads and use submissions in journeys. [Learn more](https://docs.pushwoosh.com/product/content/cloud-pages/) ![Content section in sidebar: expandable menu with Push Presets, Email Content, Ad inventory, In-Apps, Inbox Presets, Web content, Messenger presets, Vouchers, Media storage, Product Catalog, News Feeds, and Cloud Pages](https://docs.pushwoosh.com/pushwoosh-interface-overview-7.webp) ### Statistics The **Statistics** section lets you track performance and gain insights into your campaign effectiveness. In the Statistics section, you can find: * **Dashboards**: Create and view custom visual reports with key metrics and KPIs. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) * **Retention**: Track cohort analysis and retention curves. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics) * **Aggregated messages**: Analyze and review the combined statistics of multiple messages grouped into a single report for deeper insights into campaign performance. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages) ![Statistics section in sidebar: menu with Dashboards, Retention, and Aggregated messages](https://docs.pushwoosh.com/pushwoosh-interface-overview-8.webp) ### Settings The **Settings** section in the navigation sidebar is an expandable menu that contains all your project configuration tools. In the Settings section, you’ll find all the essential tools to set up and manage your Pushwoosh project. Here’s what you can do: * **Configure Platforms**: Set up credentials like API keys and certificates, and configure messaging platforms (iOS, Android, Web, Email, etc.). You can also modify your project settings by clicking **Edit project** at the top of the page, or delete the project. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) Email For email, you can also set up the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) so users choose which email categories they receive. * **Message delivery settings**: Manage delivery preferences and set up frequency capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/) * **3rd party integrations**: Connect Pushwoosh with external tools and services. [Learn more](https://docs.pushwoosh.com/product/integrations/) * **Control groups**: Exclude a share of your audience from campaigns for unbiased measurement. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups) * **API Access**: Manage API tokens and access credentials. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token) * **Deep Links**: Configure deep linking for push notifications. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) * **Test Devices**: Manage devices for testing campaigns. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices) * **Application Permissions**: Set up permissions and access control for your project. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts#app-specific-permissions) ### Notification center (sidebar) Check the **Notification center** in the sidebar to see alerts about your projects, such as warnings and critical issues. The badge shows how many unread notifications you have. For new features and improvements, see [Product updates](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#product-updates) instead. Each alert shows the date and a description. Click **View** to see details or **Expand** to read the full message. ![Notification center in sidebar showing critical and warning alerts](https://docs.pushwoosh.com/pushwoosh-interface-overview-12.webp) ### My account Manage your Pushwoosh account settings, users, and billing. In the **My account** section, you can manage your personal and organizational settings: * **My account**: Update your profile, security preferences, and notification settings. Enable two-factor authentication for added security. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/account-settings) * **Users and groups**: Invite team members, assign roles and permissions, and review login history. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts) * **Billing**: View usage, manage subscriptions, and handle payments for your account. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/billing-page) * **Referral program**: Invite others to Pushwoosh and earn rewards for successful referrals. ![My account in sidebar: menu with account options including Billing, Users And Groups, Referral Program, and Log Out](https://docs.pushwoosh.com/pushwoosh-interface-overview-10.webp) --- # Get started with your project **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/ Set up your Pushwoosh project with push, inbox, in-app, Web popups, email, SMS, WhatsApp, LINE, Kakao, Telegram, and Wallet passes. --- Begin your journey with Pushwoosh by setting up your project and activating messaging channels including push notifications, inbox, emails, in-app messages, Web popups, WhatsApp, LINE, Kakao, Telegram, SMS, Apple Wallet passes, and Google Wallet passes. Tip Let our Customer Success Team guide you through the onboarding process in Pushwoosh: from setting up projects to sending your first communications. [Youtube Video: Onboarding process in Pushwoosh: from setting up projects to sending your first communications](https://youtube.com/watch?v=Qe2mcMZ51wQ) Follow the steps below to configure each messaging channel. ## Prerequisites Before you start working with any communication channel, complete these steps: * **Create a Pushwoosh account** * **Create a project** You create your first project right after you register. Use one project for one app across multiple platforms, or create separate projects for different apps [Learn how to create a project ](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/) ## Set up messaging channels Developer assistance needed Please involve your development team to implement technical integrations. Share the relevant setup links with them. ### Push notifications Send timely and personalized alerts to users across mobile and web platforms. To enable push notifications: 1. **Configure your platform** Set up credentials for iOS, Android, Huawei, and Web. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/configure-project/) 2. **Integrate the SDK** Add the Pushwoosh SDK to your app or website. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Test the integration and publish your app or website**\ Ensure push functionality works as expected. * For apps, release the version with integrated SDK to the app stores.\ For websites, just update your live site. [Learn more](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/) 4. **Register users** Subscribe devices and sync user data to enable push delivery. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) 5. **Set custom User IDs**\ Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) ##### Next steps * **Create push content** Configure push presets and customize the message content to fit your campaign needs. [Learn more ](https://docs.pushwoosh.com/product/content/push-presets/) * **Target specific audiences** Deliver personalized messages to specific user groups based on tags, behavior, or preferences. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Send notifications** Use one-time, scheduled, recurring, or targeted messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/#start-sending-different-types-of-push-notifications/) * **Use advanced features** Enable silent push, interactive buttons, deep links, or Live Activities. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/#enhance-push-notifications-with-advanced-features/) * **Analyze results** Track delivery, opens, and campaign performance. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/) * **Manage subscribers** Keep your subscriber list clean by removing invalid or inactive push tokens. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/manage-push-subscriber-base/) ### Message inbox Store important messages in a dedicated in-app space to reach users even if they opt out of push notifications. To enable Message inbox: 1. **Configure platforms** Set up your app for push messaging on supported platforms (iOS, Android, Web). [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/configure-project/) 2. **Integrate the SDK** Add the Pushwoosh SDK to your app to enable inbox functionality. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Register users** Subscribe users and sync data to allow message delivery to the inbox. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) 4. **Set custom User IDs**\ Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) 5. **Implement Message inbox** Follow integration guides for mobile and web to embed inbox functionality in your app. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/#implement-message-inbox-in-your-app) 6. **Customize the UI (optional)** Use default inbox UI or fully customize it to match your app’s design\ [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/#customize-the-message-inbox-ui) ##### Next steps * **Define message visibility** Control which user segments see inbox messages based on app activity or tags. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Manage inbox messages** Define which messages are saved to the inbox and configure their retention settings. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/) * **Track performance** Monitor inbox message engagement via Customer Journey statistics or API. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/#track-and-analyze-message-inbox-performance/) ### In-app messages Display messages directly inside your app to engage users in real-time. To enable in-app messaging: 1. **Configure your platform** Ensure your app is set up for in-app messaging on supported platforms. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/configure-project/) 2. **Integrate the SDK** Required for triggering and displaying in-app content. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Test the integration and publish your app** Ensure in-app functionality works as expected and release the version with the integrated SDK to the app stores. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/) 4. **Register users** Sync user data and ensure devices are subscribed. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) 5. **Set custom User IDs**\ Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) ##### Next steps * **Create in-app messages** Create in-app messages using Pushwoosh no-code editor [Learn more ](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) * **Display context-specific messages** Show in-app messages to users based on their actions, app state, or profile data. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Send in-apps** Deliver instantly or include them in Customer Journey campaigns. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/) * **Measure impact** Review impressions, clicks, and engagement metrics. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) ### Web popups Show targeted overlay messages on your website to engage visitors while they browse. To enable Web popups: 1. **Integrate the Web Push SDK and turn on Web popups** Your development team adds the Web Push SDK to your site and enables Web popups in the SDK settings. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#initialize-the-sdk) 2. **Create web content** Open **Content → Web content** and design the popup you will use in campaigns. [Learn more](https://docs.pushwoosh.com/product/content/web-popup-content/) 3. **Create a Web popup campaign** Go to **Campaigns → Web campaigns**, click **Create campaign**, and configure content, audience, and display settings. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) ##### Next steps * **Learn more about Web popups** [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) ### Emails Reach your users with personalized email campaigns or transactional messages. To start sending emails: 1. **Configure email platform** Authenticate your domain and email address. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) 2. **Collect email addresses** Collect user email addresses by adding opt-in forms, subscription checkboxes, or uploading existing contacts. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/get-email-subscribers/) 3. **Register users** Add email addresses to your user base and sync user data. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) 4. **Set custom User IDs** Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) ##### Next steps * **Set up the subscription preference center** Configure the subscription preference center so users choose which email categories they receive. Only opted-in users get emails for each category. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) * **Create email content** Design and personalize email content. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/emails/#create-and-customize-email-content/) * **Use segmentation** Segment your audience by demographics, activity, or preferences for tailored communication. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Send emails** Launch email campaigns or transactional messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/) * **Track performance** Monitor delivery, opens, clicks, and bounces. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) ### WhatsApp Engage users on one of the world’s most widely used messaging apps, delivering secure and approved messages that drive meaningful interactions. To use WhatsApp messaging: 1. **Connect your WhatsApp Business account**\ [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) 2. **Approve templates** Submit your own message templates for approval in Meta, or use pre-approved templates provided by Meta. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/#create-whatsapp-templates) 3. **Import user WhatsApp contacts**\ [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp/) 4. **Set custom User IDs**\ Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) ##### Next steps * **Reach the right users** Deliver messages to specific audience segments based on subscription status or user profile. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Send campaigns** Target users with relevant WhatsApp communications. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/) ### SMS Send concise messages straight to users’ mobile devices for immediate visibility and action. To start sending SMS messages: 1. **Connect your SMS provider**\ [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) 2. **Register users’ phone numbers**\ Ensure phone numbers are collected, formatted correctly (E.164), and users have opted in. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms/import-sms-contacts/) 3. **Set custom User IDs**\ Assign a custom User ID to unify a person’s devices/contacts under one profile. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/) ##### Next steps * **Create SMS content** Create reusable and customizable SMS presets to fit a variety of campaign needs. [Learn more ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) * **Deliver timely messages to the right audience** Ensure messages reach relevant users based on opt-in status, tags, or campaign-specific criteria. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) * **Send SMS messages** [Learn more ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/) * **Track performance** Monitor delivery status, failures, and user engagement [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#sms/) ### Telegram Send Telegram messages in Customer Journey using your connected Telegram bot. To start using Telegram messaging: 1. **Connect Telegram to Pushwoosh**\ Configure Telegram in **Settings > Configure Platforms** and add your bot API token. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/) 2. **Send Telegram messages in journeys** Launch Telegram messages from Customer Journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/) ### LINE Use LINE to connect with users via Customer Journeys, API, or 1:1 chat, using either templates or plain-text messages. To start using LINE messaging: 1. **Set up your LINE accounts**\ Create a [LINE Official Business Account](https://www.linebiz.com/jp-en/) and a [LINE Developers Account](https://developers.line.biz/en/docs/line-developers-console/login-account/). 2. **Connect LINE to Pushwoosh**\ Configure the LINE platform in your Pushwoosh account. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/) 3. **Make sure LINE Messaging is set up and billing is enabled.** > **Important:** LINE Messaging API pricing is managed directly by LINE. Plans vary by region and usage volume. [View pricing details](https://developers.line.biz/en/docs/messaging-api/pricing/) ##### Next steps * **Create message presets** Build and reuse LINE message presets in Pushwoosh for consistent, formatted messaging. [Learn more ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) * **Automate campaigns** Use LINE elements in Customer Journeys to send messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/line/send-line-messages/#send-line-messages-via-customer-journey/) * **Send messages via API** Use the Pushwoosh API to send LINE messages with template or plain-text content. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/line-api/) * **Chat in 1:1 format** View message history and send replies to users you’ve previously contacted through the LINE Messages interface. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/line/send-line-messages/#send-free-form-messages-and-view-your-line-conversations/) ### Kakao Send Kakao messages via Customer Journeys or API using approved templates to reach users on KakaoTalk. To start using Kakao messaging: 1. **Connect Kakao to Pushwoosh**\ Configure the Kakao platform in your Pushwoosh account. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration/) 2. **Create and approve message templates** Use approved Kakao templates; custom templates require approval in the Kakao Business Center. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) ##### Next steps * **Automate campaigns** Use Kakao elements in Customer Journeys to send messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/kakao/send-kakao-messages/) * **Send messages via API** Use the Pushwoosh API to send Kakao messages with approved templates. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/kakao/) ### Apple Wallet passes Design Apple Wallet passes, including loyalty cards, coupons, event tickets, and more, and push live updates to every device that added them. To start using Apple Wallet passes: 1. **Configure the signing certificate** Add your Apple Pass Type ID certificate to your application so Pushwoosh can sign passes. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) 2. **Design and issue a pass** Build a pass in the pass builder, then share it as an install link or QR code. [Learn more](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) ##### Next steps * **Distribute and update passes in the Control Panel.** [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) * **Message pass holders** Reach holders with a one-time push, pass update, or Customer Journey. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/) * **Manage passes via API** Use the PassKit API to create, update, and manage passes programmatically. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) ### Google Wallet passes Design Google Wallet passes, including loyalty cards, offers, event tickets, and more, and let Google sync updates to every device that saved them. To start using Google Wallet passes: 1. **Configure Google Wallet in Pushwoosh** Invite the Pushwoosh service account to your Google Wallet issuer and add your Issuer ID to your application. This setup is independent of Firebase. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) 2. **Design and issue a pass** Build a pass in the pass builder, then share an “Add to Google Wallet” save link or QR code. [Learn more](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) ##### Next steps * **Distribute and update passes in the Control Panel** [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) * **Message pass holders** Reach holders with pass updates, one-time push, or Customer Journey. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/) * **Manage passes via API** Use the Google Wallet API to create passes, issue save links, and send them to users. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/) ## Build omnichannel campaigns Once you’ve configured each messaging channel, you can begin creating omnichannel campaigns. These campaigns let you reach users across multiple channels using a unified strategy, ensuring consistent and personalized communication. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) --- # Configure platforms **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/ Configure platforms in Pushwoosh for iOS, Android, web, email, messengers, SMS, Apple Wallet, and Google Wallet passes. --- To enable Pushwoosh to send messages to your users, it’s essential to configure the platforms you plan to work with. Developer assistance needed You will need assistance from your development team to set up certain platforms, including mobile, web, or email configurations. To start configuring a platform, go to **Settings → Configure platforms** and click **Connect** next to the required platform: ![Configure platforms page in Pushwoosh showing platform list and Connect buttons.](https://docs.pushwoosh.com/configure-platforms.webp) Demo project Platform configuration is not available in the Demo project. The Demo project is separate from the projects you create yourself. Configure platforms in your own project instead, where the **Configure platforms** page lists the platforms and their **Connect** buttons as usual. [Learn how to create or edit your project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/) ## Mobile Developer assistance needed Configuring the mobile platform requires support from your development team. Share the links below with them for detailed guidance. ### iOS configuration * [iOS certificate-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration) * [iOS token-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration) * [Apple Wallet pass configuration for iOS](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) ### Android Firebase configuration * [Android Firebase configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * [Google Wallet pass configuration for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) ### Amazon configuration * [Amazon Device Messaging configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/amazon-configuration/) ### Huawei configuration * [Huawei configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration) ## Web Developer assistance needed Configuring the web platform requires support from your development team. Share the link below with them for detailed guidance. * [Web push configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/) ## Desktop platforms Developer assistance needed Configuring desktop platforms requires support from your development team. Share the links below with them for detailed guidance. * [Windows configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/windows-configuration) * **macOS** ## Email Developer assistance needed To send emails, set up your domain and email address in Pushwoosh. While this process may not always require developer assistance, consulting your IT team may be necessary for domain verification. Share the link below with them for detailed guidance. [Email configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification) ## WhatsApp Add your WhatsApp Business Account credentials directly in the Control Panel to enable WhatsApp messaging. * [WhatsApp configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) ## LINE Developer assistance needed Configuring the LINE platform may require assistance from your development team. Share the link below with them for detailed guidance. Reach one of the most popular messaging audiences by connecting your LINE account to Pushwoosh. Once configured, you can send personalized LINE messages. Use Customer Journey or API to expand your communication strategy and boost engagement. * [LINE configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/) ## Kakao Connect your KakaoTalk account to Pushwoosh to send messages to users in South Korea and other regions. Use approved templates in Customer Journey or via API. * [KakaoTalk platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration) ## Telegram Connect a Telegram bot to your Pushwoosh project to send Telegram messages in Customer Journey. * [Telegram platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration) ## Viber Connect your Viber account to Pushwoosh to send Viber messages in Customer Journey using Rakuten-approved templates or freeform text. * [Viber platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/viber-configuration) ## SMS Connect your own SMS provider directly in the Control Panel to enable SMS messaging. Once configured, you can send SMS in one-time campaigns and Customer Journeys. * [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration) * [SMS provider webhooks](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/sms-provider-webhooks/) (Developer docs: provider callback URLs) ## Edit project Click **Edit project** at the top right of the **Configure platforms** page to open project settings. There you can change the project title and icon, connect your bit.ly account to shorten links in push notifications, and manage email notification preferences. Tip **Connect your bit.ly account to shorten links** Push payload size is limited, so the best practice is to shorten the links you use in your push notifications. In the **Edit project** dialog, check **I want to add my bit.ly account** and enter your bit.ly access token in the **ACCESS TOKEN** field. Using your own account assures all your messages are shortened and sent properly as you control the shortener limits yourself. [Learn more about editing your project](https://docs.pushwoosh.com/product/account-management-and-security/managing-projects/#editing-projects) ## Hide unconfigured platforms At the bottom of the **Configure platforms** page, use **Hide unconfigured platforms** to show only the platforms you have already set up. This keeps the list short when you work with a subset of channels. ## Delete project To delete a project, go to **Settings → Configure platforms**, open the three-dot menu at the top right of the page (next to **Edit project**), and select **Delete project**. Caution Deleting a project is permanent. The project and all its user base will be removed from your account. This action cannot be undone. For more on project management, see [Manage projects](https://docs.pushwoosh.com/product/account-management-and-security/managing-projects). --- # Email platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/ Set up your email platform to send secure campaigns with verified domains, trusted sender details, and link tracking. --- Note Please note that platform configuration is project-specific. Configure the Email platform for each project you want to use for sending emails. Verified emails and domains are available for all projects in your account. To start sending emails through Pushwoosh, you need to complete setup steps. These steps verify your account, configure sender details, and authenticate your domain. This process ensures secure, compliant email delivery and enables accurate tracking of engagement metrics. ## Verify your account The first step is to verify your account. This is essential to ensure the reliable and secure delivery of your emails. Verification helps prevent platform misuse for spam or malicious activities and ensures compliance with regulations. Follow these steps to complete the verification process: 1. Navigate to **Settings > Configure platforms**. 2. Locate **Email message** and click **Configure**. ![Email platform configuration interface showing the Configure button for email message settings.](https://docs.pushwoosh.com/configure-project-email-verification-1.webp) 3. In the form that appears, provide the URL of your company’s website in the **Your company website URL field** (e.g., ). ![Account verification form showing fields for company website URL and other required information.](https://docs.pushwoosh.com/configure-project-email-verification-2.webp) 4. In the **Primary technical contact** field, enter the email address of the person who manages the technical aspects of your integration. This contact will be used for technical inquiries or issues. 5. In the **Tell us about how you’ll be using Pushwoosh** field, provide details on how you intend to use Pushwoosh for your email campaigns. Include information such as: * The types of messages you plan to send (e.g., newsletters, promotional offers, etc). * How your subscribers sign up to receive these messages (e.g., website form, mobile app registration). After filling out all the information, click **Verify my account** to submit your request. Pushwoosh will review your details and respond as soon as possible. After your account is verified, you will receive a confirmation email. You can then proceed to configure your email sender settings. For this: 1. Go to **Settings**. 2. Select **Configure platforms**. 3. Click **Continue configuration** next to the email platform. ![Platform configuration interface showing the Continue configuration button for the email platform.](https://docs.pushwoosh.com/configure-project-email-verification-3.webp) ## Configure sender details ### Add a domain To send emails with Pushwoosh, add and verify a domain. In the **Domain** field, enter the domain you wish to use for sending emails (e.g., yourcompany.com). This domain must be verified via DNS in the next step before it can be used in your email campaigns. ![Domain configuration interface showing the field for entering a sender domain.](https://docs.pushwoosh.com/configure-project-email-verification-4.webp) Tip You will see the remaining number of domains you can add at the top of the screen. To increase this limit, please [contact our support team](https://help.pushwoosh.com/hc/en-us/) ### Add default sender details ##### From address Set up a default sender name and email address that will appear in recipients’ inboxes. This helps users recognize the sender and builds trust. * **Name**: Enter the sender’s name (e.g., John). * **Email**: Enter the sender’s email address before the ”@” symbol. For example, for **, enter *marketing*. How sender details are determined in customer journeys and one-time emails These default sender details are used when no sender is set in the email content or in the journey (or one-time email) step. Sender set in a [Customer Journey step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#define-sender-details-and-reply-address) or in the [one-time email form](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#sender-details) overrides both the email content and the platform default. Sender set in the email content overrides the platform default. ![Default sender details configuration form showing fields for sender name and email address.](https://docs.pushwoosh.com/configure-project-email-verification-5.webp) Select **Use this as reply-to address to track replies** checkbox if you want replies from recipients to go directly to the sender’s email address. ##### Reply to address To use a different reply-to address, leave the checkbox unchecked, and specify: * **Reply-To name**: The name that will appear as the contact for replies. * **Reply-To email**: The email address where replies should be sent. ![Reply-to address configuration form showing fields for reply-to name and email address.](https://docs.pushwoosh.com/configure-project-email-verification-6.webp) If both fields are left blank, replies will be sent to the default sender’s email specified in the **From address** section. After completing the required fields, click **Next** to proceed with domain verification. ## Configure DNS records Technical assistance may be required *** Adding DNS records requires access to your domain’s DNS settings. If you’re unsure how to configure them, ask your IT team or domain provider for assistance. To authenticate your domain and enable email sending for your campaigns, configure the following DNS records in your hosting provider’s DNS settings: * **DKIM (Required)**. This record digitally signs your emails, allowing email providers to verify that an email originates from your domain and has not been altered in transit. * **SPF (Required)**: This record specifies which mail servers are permitted to send emails on behalf of your domain, helping to prevent spoofing. * **DMARC (Required)**: This record provides instructions for handling unauthenticated emails and reporting back to you, enhancing email security. ### DKIM records (required) What are DKIM Records? DKIM (DomainKeys Identified Mail) is an email authentication method that lets receiving servers verify that an email was sent by the stated domain and has not been altered in transit. By adding DKIM records to your DNS settings, you apply a cryptographic signature to outgoing emails. This signature verifies that the message’s content remains unchanged and authenticates it as a legitimate message from your domain. Copy the CNAME records and add them to your DNS settings. To set up DKIM, you’ll need to add **all four CNAME (Canonical Name) records** to your DNS settings. These records are essential for authenticating your domain and confirming that emails sent from your domain are authorized. ![DKIM configuration interface showing the four CNAME records that need to be added to DNS settings.](https://docs.pushwoosh.com/configure-project-email-verification-8.webp) Note DNS providers handle records differently. Some require you to paste the entire record (host name + domain), while others only need the unique key portion (host name without the domain). Check your DNS provider’s instructions on how to add the records. If a record isn’t set up correctly, a note under it explains the problem, for example: * The record is missing. * The record points to the wrong value. * The record still has your domain name appended twice. * The record was published as a TXT record instead of a CNAME. * DNS didn’t respond. Check again in a few minutes. Once you’ve added the required DNS records, the domain verification process usually takes about 5 to 10 minutes, but it can take up to 24 hours in some cases. During this time, you won’t be able to send emails through Pushwoosh until the verification is complete. In the meantime, you can continue with other setup steps and return later to check the verification status. Click **Verify DNS records** to refresh the verification status after adding the DNS records. Once verification is successful, the Domain verification status will change to **Verified**. ![Domain verification status interface showing the Verify DNS records button and verification status.](https://docs.pushwoosh.com/configure-project-email-verification-9.webp) #### Send a test email Once your domain configuration and DKIM verification are complete, you can send a test email to ensure your email settings are functioning correctly and that emails will be successfully delivered to users once your campaign goes live. To send a test email: 1. In the **Email Channel Configuration** section, click **Send test email**. ![Email Channel Configuration interface showing the Send test email button.](https://docs.pushwoosh.com/configure-project-email-verification-10.webp) 2. In the form that opens, enter Sender and Recipient details: * **From**: The sender’s email address you configured. * **Recipient**: Enter the email address where you want to send the test email. By default, this field is pre-filled with the email of the logged-in user, but you can change it if needed. 3. Select the email content from the dropdown to include in the test email. 4. Click **Send**. ![Send test email form with From, Reply-to, Recipient and email content fields, and the Send button.](https://docs.pushwoosh.com/configure-project-email-verification-11.webp) Sending fails if the domain isn't verified If the “From” or reply-to domain isn’t fully verified, creating the test email now fails right away with a **From email domain is not verified** or **Reply email domain is not verified** error, instead of the test email being created but silently dropped when it’s actually sent. Check the domain’s status in [Your sender domains](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#your-sender-domains), complete any DNS records still marked **Pending**, and try again. ### SPF records (required) Pushwoosh requires setting up a **Sender Policy Framework (SPF) record** to authorize its mail servers to send emails on behalf of your domain. SPF is an email authentication protocol that helps prevent spoofing and improves email deliverability. Note Before adding a new SPF record, check if your domain already has one, as most DNS providers allow only one SPF record per domain. #### Adding a new SPF record If your domain does not have an SPF record, add a new TXT record with the following value. **SPF Value** ```plaintext "v=spf1 include:spf.pushwoosh.io ~all" ``` Depending on your DNS provider, the name of the record may be either blank or set as @. #### Adding to an existing SPF record If your domain already has an SPF record, simply add the following SPF mechanism to your existing record to authorize Pushwoosh. ```plaintext include:spf.pushwoosh.io ``` ![SPF record configuration interface showing the SPF value and instructions for adding DNS records.](https://docs.pushwoosh.com/configure-project-email-verification-12.webp) If your existing SPF record doesn’t include Pushwoosh’s servers, or the domain has more than one SPF record (which causes receivers to ignore all of them), a note below the record explains which of these problems was found. ### DMARC verification (required) **DMARC** (Domain-based Message Authentication, Reporting, and Conformance) is an email authentication protocol. It helps protect your domain from unauthorized use, such as email spoofing. Pushwoosh requires DMARC to be configured on your domain to ensure secure and authenticated email delivery. For more information on DMARC, see the [DMARC Protocol Documentation](https://dmarc.org/overview/). The DMARC record Pushwoosh generates for you uses a monitor-only policy (`p=none`): unauthenticated mail is still delivered and reported on, not quarantined or rejected. If your domain already has a DMARC record with a stricter policy, keep your existing one instead of replacing it. To configure DMARC: 1. Copy the DMARC TXT record shown on this step. ![DMARC record card showing the verified DMARC TXT record with the v=DMARC1; p=none; value to add to DNS settings.](https://docs.pushwoosh.com/configure-project-email-verification-13.webp) 2. Go to your DNS provider’s settings and add the DMARC record to the DNS settings. Please, refer to your DNS provider’s documentation if you need help adding these records. 3. Enable reporting (optional). To receive reports on email authentication and security issues, add an email address in the `rua` field within the **Value** parameter. ### Check domain status and fix issues After you add the records, use the buttons above the DNS record list: * **Verify DNS records**: click after you add or change records to refresh their status. * **Send test email**: check that sending works once the domain is configured (see [Send a test email](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#send-a-test-email)). * **Copy instructions**: copy all required DNS records with setup notes as plain text, to paste anywhere. * **Email to your IT team**: open your email client with the same instructions filled in, ready to send to whoever manages your DNS. ![DNS records for a verified domain, showing the green all-required-records-verified banner above the Verify DNS records, Send test email, Copy instructions, and Email to your IT team buttons.](https://docs.pushwoosh.com/configure-project-email-verification-19.webp) If something needs your attention, a banner above the records says what’s wrong: * **Some required DNS records are missing or wrong**: check the note under each record for the specific fix. * **DNS records are in place**: Pushwoosh found your records and is confirming them with the mail provider, a separate step from the DNS propagation described under [DKIM records](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required). This usually takes up to 30 minutes, and you don’t need to do anything. * **Verification stopped before the records were added**: your records are correct now, but the earlier verification attempt expired. Click **Restart verification** in the banner. Your SPF and DMARC records stay as they are, and the DKIM records usually stay the same too. Check the [DKIM records](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required) after restarting in case they changed. * **This domain does not exist in DNS**: the domain name isn’t registered or can’t be found. Check the spelling, or register the domain first. When the banner names a DNS provider (for example, “DNS for this domain is managed at …”), add the records there rather than at your domain registrar. ### Choosing a different domain If you realize that you need to change your domain at this step, click **Choose different domain**. Then, you can add a new sender domain. Keep in mind that changing your domain name will generate new DNS records. This ensures that the domain limit is tracked correctly and excludes any domains you no longer need. ![Domain selection interface showing the Choose different domain button for changing sender domains.](https://docs.pushwoosh.com/configure-project-email-verification-14.webp) ## Configure link tracking Link tracking helps you understand how your audience interacts with links in your emails by adding **UTM parameters**. With UTM tags, you can track email engagement directly in Google Analytics or any other analytics system that tracks UTM. Note If you choose not to configure Google Analytics URL tracking, Pushwoosh will still track link clicks and provide basic statistics. Simply leave the **Google Analytics URL tracking** toggle off and click **Complete setup**. To enable link tracking, turn on the **Google Analytics URL tracking** toggle. The following UTM parameters will be added to your links: * **utm_source**: Identifies the traffic source. * **utm_medium**: Specifies the marketing medium, which is set to “email” by default. * **utm_campaign**: Adds the campaign code (e.g., campaign ID). This is inserted automatically. * **utm_content**: Tracks the specific content within the email, such as the subject. This is inserted automatically. The **Link Preview** shows an example of how your links will look with the selected UTM parameters. ![Link tracking configuration interface showing UTM parameters and link preview example.](https://docs.pushwoosh.com/configure-project-email-verification-15.webp) 3. Once you’ve configured the UTM parameters, click **Complete setup** to save your settings. The UTM tags will be added automatically to every link in your email, allowing you to track the source, medium, campaign, and content performance directly in Google Analytics. ## Review and manage your email channel settings Once your email channel is configured, the **Email channel configuration** dashboard provides an overview and allows you to manage key settings for sending emails, including sender domains, default sender details, and link tracking options. ### Configuration status At the top, you can view the email platform status, which indicates whether it is configured. If the platform is not configured, it may be due to the following reasons: * **Default sender details** have not been specified. [Learn more about default sender details](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification#add-default-sender-details). ![Default sender details not specified](https://docs.pushwoosh.com/configure-project-email-verification-16.webp) * An **unverified domain** is set as the default sender. [Learn more about verifying your domain](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification#configure-dns-records). ![Email platform status showing unverified domain warning message.](https://docs.pushwoosh.com/configure-project-email-verification-17.webp) ### Your sender domains In the **Your sender domains** section, you can view and manage the domains you use for sending emails and the status of each domain: * **READY**: The domain is configured and ready to send emails. * **NOT READY**: The domain verification is incomplete, typically due to pending [DKIM verification](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required), which is required. Domains marked as **NOT READY** cannot send emails until all necessary verifications are completed. To complete the configuration, click on the domain name in the list. Additionally, this section displays the status of each verification record (DKIM, SPF, DMARC) as **Verified** or **Pending**. **Pending** indicates that the record is either awaiting verification or has not yet been configured. If a domain still shows **Pending** even though you’ve added DNS records, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to confirm which records your account needs. The **Gmail Postmaster** column shows whether each domain is connected to Gmail Postmaster. See [Connect your domain to Gmail Postmaster](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#connect-your-domain-to-gmail-postmaster). Click **Update status** to refresh the verification status for each domain. ##### Adding a domain To add additional domains, click **Add domain**. To increase this limit, please [contact support](https://help.pushwoosh.com/hc/en-us/requests/new). ![Domain management interface showing the Add domain button for adding additional sender domains.](https://docs.pushwoosh.com/configure-project-email-verification-18.webp) ##### Deleting a domain Important *** Deleting a domain can disrupt active email campaigns that use it as the sender. Be sure to check all ongoing campaigns before deleting the domain. To delete a domain: 1. In **Your Sender Domains**, locate the domain to delete. 2. Click the **three-dot menu** next to the domain name and select **Delete domain**. 3. Confirm deletion, or click **Cancel** to retain the domain. **Note**: If a domain is set as the default [“From” address](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#from-address) in any project, you cannot delete it until the sender information is updated in those projects. An error message will appear, along with a list of projects using this domain as the default sender. Update the “From” address in each project to a different verified domain, then try deleting the domain again. ##### Sending a test email From the list of domains, you can also send a test email. To do this, click the three-dot menu next to the configured domain and select **Send Test Email**. Note that this option is only available for domains that have been fully configured. ### Default sender details In this section, you can view and manage the default sender details, including: * **From Address**: The name and email address that will appear as the sender in emails. * **Reply-To Address**: The default name and email for replies. To make changes, click **Edit default sender details**. Important *** The email channel will not be configured if you don’t specify the [default sender details](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#add-default-sender-details), and a warning message will appear. To set default sender details, click **Set sender details.** ### Google Analytics URL tracking In this section, you can manage [**Link Tracking**](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#configure-link-tracking) settings. To enable tracking, switch the toggle to **Enable**. When enabled, UTM parameters are automatically added to email links, allowing you to track user engagement in Google Analytics. You’ll also see a **Link Preview** displaying how your URLs will appear with UTM tags. ## Connect your domain to Gmail Postmaster Gmail Postmaster shows how Gmail rates mail from your sender domain: the share of mail Gmail users report as spam, how much mail Gmail rejects and why, and whether the domain meets Gmail’s sender requirements. Connecting is optional, and emails send the same way without it. Once the domain is connected and verified, its Gmail data appears on the **Email** tab of **Project overview** (the block calls it **Home › Email**), see [How Gmail sees your domain](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#how-gmail-sees-your-domain). Before you start, make sure that: * The domain’s [DKIM records](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required) are verified. Pushwoosh doesn’t connect a domain until they are. * You can add a TXT record in the domain’s DNS settings, or you have someone who can. Note Gmail Postmaster is available only in some Pushwoosh regions. If **Your sender domains** has no **Gmail Postmaster** column, it isn’t available in the region where your account is hosted. If you’re not sure where your account is hosted, contact your Customer Success Manager or [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). ### Connect a domain 1. Go to **Settings > Configure Platforms**. 2. Click **Configure** next to **Email message**. 3. In **Your sender domains**, click **Connect** in the **Gmail Postmaster** column next to the domain. The domain’s DNS records panel opens on the **Gmail Postmaster** block. ![Your sender domains list with the Gmail Postmaster column showing Verified, Waiting for Google, and a Connect button.](https://docs.pushwoosh.com/configure-project-email-verification-23.webp) 4. In the **Gmail Postmaster** block, click **Connect Gmail Postmaster**. ![DNS records panel of a verified sender domain with the Gmail Postmaster block and its Connect Gmail Postmaster button.](https://docs.pushwoosh.com/configure-project-email-verification-20.webp) 5. Copy the TXT record value the block shows. It starts with `google-site-verification=`. 6. In your DNS provider’s settings, add a TXT record with the host `@` (the domain itself) and the copied value. ![Gmail Postmaster block in Waiting for Google status, showing the TXT record with host @ and a google-site-verification value.](https://docs.pushwoosh.com/configure-project-email-verification-21.webp) If the domain already has a `google-site-verification` TXT record, for example from Google Search Console, add the Pushwoosh record next to it. Don’t replace the existing one, because the other service stops working without it. You don’t need to come back and confirm anything. Pushwoosh checks the record every hour for the first 3 days, then once a day. To check right away after you add the record, click **Verify DNS records**. DNS changes can take up to 48 hours to reach Google. ### Gmail Postmaster statuses The **Gmail Postmaster** column in **Your sender domains** and the status badge after the **Gmail Postmaster** title in the block show where the connection stands. Click the status in the column to open the domain’s **Gmail Postmaster** block. * **Connect** in the column, no status badge in the block: the domain isn’t connected. Click **Connect Gmail Postmaster** in the block to start. * **Waiting for Google:** Pushwoosh is waiting for Google to confirm the TXT record. The note under the record says what Pushwoosh sees in your DNS. * Pushwoosh hasn’t been able to check the record yet. Wait, no action is needed. * The record is found, and Google hasn’t confirmed it yet. Wait, no action is needed. * The record isn’t found. Check that the host is `@` and that you copied the whole value. * Another `google-site-verification` record is found. Add the Pushwoosh record next to it. * **Verified:** Google confirmed the domain. Keep the TXT record in DNS. If you remove it, or the domain’s own DNS records stop being verified, the status goes back to **Waiting for Google** and Gmail data stops updating. * **Not verified:** Pushwoosh didn’t find the record for 30 days and stopped checking. Add the record, then click **Check again** to restart the checks for another 30 days. ### Gmail sender requirements When the domain is verified, the block lists three requirements that Gmail checks on top of your DNS records. Each one shows **Compliant** or **Needs work**, as Gmail assesses it: * **From: header aligned with SPF or DKIM domain.** The domain in the **From** address matches the domain that SPF or DKIM authenticates. If it needs work, Gmail sees mail whose **From** domain matches neither. * **One-click unsubscribe:** your mail carries a header that lets recipients unsubscribe in one click from the Gmail interface. See [List-Unsubscribe header support](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#list-unsubscribe-header-support). * **Honor unsubscribe:** mail stops going to people who unsubscribed. If it needs work, Gmail still sees mail going to unsubscribed recipients. ![Verified Gmail Postmaster block with the TXT record and three Gmail sender requirements, one marked Needs work.](https://docs.pushwoosh.com/configure-project-email-verification-22.webp) Pushwoosh reads the requirements from Gmail once a day, and only those Gmail has already assessed. Right after verification, the list can stay empty for a day or longer. ### Disconnect a domain You can disconnect a verified domain. 1. In the domain’s **Gmail Postmaster** block, click **Disconnect**. 2. Confirm by clicking **Disconnect** in the dialog. Gmail data for the domain stops updating on **Project overview**. After that, you can remove the TXT record from DNS. Deleting a sender domain disconnects it from Gmail Postmaster as well. ## Turn on the sunset policy The sunset policy stops marketing emails to contacts who haven’t opened or clicked your emails for a period you choose. It’s set for each project and is off by default. For how Pushwoosh decides a contact is inactive and which emails the policy skips, see [Stop marketing emails to inactive contacts](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts). To turn on the sunset policy: 1. Go to **Settings** → **Configure Platforms** and click **Platform settings** in the **Emails** row. 2. On the **Sunset policy** card, click **Enable**. ![Sunset policy card in the email platform settings with the Enable button highlighted](https://docs.pushwoosh.com/emails-email-deliverability-sunset-1.webp) 3. In **What counts as activity?**, choose **Click or open**, **Click**, or **Open**. 4. In **How long before a contact becomes inactive?**, choose 30, 90, 180, 365, or 730 days. The default is 180 days. ![Enable sunset policy window with Click or open selected and the period set to 180 days](https://docs.pushwoosh.com/emails-email-deliverability-sunset-2.webp) 5. Click **Enable**. The card now shows how many of your subscribed email contacts are inactive. The number updates once a day and after each change to the settings. ### Change the sunset policy settings 1. On the **Sunset policy** card, click **Settings**. 2. Change **What counts as activity?** or **How long before a contact becomes inactive?**. 3. Click **Save**. ### Turn off the sunset policy 1. On the **Sunset policy** card, open the menu. 2. Click **Disable…**. 3. In the confirmation window, click **Disable**. Marketing emails then go to all subscribed contacts again, including inactive ones. ## Configuring emails via Pushwoosh API You can also set up the email platform using the Pushwoosh API. For this, you’ll need assistance from your development team. Please share [this guide](https://docs.pushwoosh.com/developer/api-reference/app-configuration#configureapplication-for-email) with them for detailed instructions. ## Set up the subscription preference center Once the email platform is configured (domains verified and default sender details set), you can set up the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) so users can choose which email categories they receive. ## Set up a custom tracking domain The **Click Tracking Domain (CTD)** is the domain part of a tracked link URL shown in an email message. It combines a subdomain with your main domain, for example, `click.yourdomain.com`. The subdomain is created in your domain’s DNS settings with your domain provider, usually by adding a CNAME record. With a CTD, you can wrap email links for click tracking on your own subdomain and keep deliverability and deep links working as expected. A CTD only changes which domain those tracked links use. It doesn’t change what’s tracked: click statistics and the [click map](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#clicks-tab) work the same with or without one. To set up a custom tracking domain: 1. Decide which subdomain you will use as your CTD (for example, `click.yourdomain.com`). 2. In Pushwoosh, open **Settings**, then select **Configure Platforms**. 3. On the **Email channel configuration** page, scroll to **Custom tracking domain** and select **Enable**. Note The **Custom tracking domain** section is shown only after the email channel is configured for your application. If you do not see it, complete [email configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) first. ![Pushwoosh Control Panel showing Settings, Configure Platforms, Email channel configuration, and the Custom tracking domain card with the Enable control.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-2.webp) 4. Enter your CTD in the **Domain** field and select **Next**. ![Enable custom tracking domain dialog with the Domain field for entering the CTD and Cancel and Next buttons.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-3.webp) 5. Outside Pushwoosh, open the service where you manage public DNS for the domain of your CTD (typically your domain registrar, a DNS host, or your hosting account). In its DNS or zone editor for that domain, add the **CNAME** Pushwoosh shows on this step so your CTD points to `api.pushwoosh.com`. DNS propagation DNS changes usually take effect within minutes but can take up to 48 hours. 6. In Pushwoosh, select **Verify**. ![Custom tracking domain DNS verification step showing the CNAME target api.pushwoosh.com and Back, Verify, and Save controls.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-4.webp) 7. When verification succeeds, select **Save**. 8. If you need to change the **Domain** or DNS record, select **Back** and adjust the earlier steps. 9. After you click **Save**, your CTD appears in the **Custom tracking domain** list in **Email channel configuration**. ![Email channel configuration with the Custom tracking domain section showing the saved click tracking domain in the list.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-5.webp) To disable it later, select `...` next to your CTD, then select **Disable custom tracking domain**. --- # Facebook Messenger platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/ Learn how to connect a Facebook Page to Pushwoosh and set the webhook in your Meta App to enable Facebook Messenger messaging. --- Pushwoosh enables you to send and receive Facebook Messenger messages as part of your omnichannel campaigns. Connect your Facebook Page to Pushwoosh to use Facebook Messenger in Customer Journey and one-time messages. ## Prerequisites Before configuring Facebook Messenger, ensure the following requirements are met: * **Facebook Page:** the Page you want to message customers from. * **Meta App:** a [Meta App](https://developers.facebook.com/apps/) with the **Messenger** product added, connected to your Facebook Page. * **Page ID and Page Access Token:** generated for your Page in the Meta App dashboard. You enter those values in Pushwoosh in the steps below. For Meta App and token creation, share this page and [Meta’s Messenger docs](https://developers.facebook.com/docs/messenger-platform/) with development. ## Set up the Facebook Messenger configuration To set up the Facebook Messenger configuration, follow these steps: 1. Go to **Settings → Configure platforms**. Under **Messengers**, click **Connect** on **Facebook Messenger**. ![Configure Platforms Messengers section with the Facebook Messenger row and Connect link highlighted](https://docs.pushwoosh.com/configure-project-facebook-messenger-configuration-1.webp) 2. On the **Facebook Messenger Configuration** page, enter your **Page ID** and **Page Access Token** from the Meta App dashboard. ![Facebook Messenger Configuration page with Page ID and Page Access Token fields](https://docs.pushwoosh.com/configure-project-facebook-messenger-configuration.webp) 3. Click **Apply** to save your configuration. To discard changes, click **Cancel**. After you click **Apply**, Pushwoosh registers the inbound events it needs with Meta (`messages`, `messaging_postbacks`, `messaging_optins`, `messaging_referrals`, `message_deliveries`, `message_reads`) automatically. You do not need to choose those event types yourself in Pushwoosh. ## Set the Webhook URL in Meta To allow Meta to send incoming messages and events to Pushwoosh, configure a webhook URL in your Meta App dashboard. Follow these steps to set it up: 1. In your Meta App dashboard, go to the **Messenger > Settings** page. 2. In the **Webhook** section, enter the following callback URL: ```plaintext https://tw-callback.svc-nue.pushwoosh.com/api/v1/fbMessengerCallback ``` 3. Enter the verify token you configured for this Page, then click **Verify and Save**. 4. Subscribe the webhook to your connected Page in Meta (the same Page you connected in Pushwoosh). This is separate from the inbound event fields Pushwoosh registers after **Apply**. [Refer to the official Meta documentation for details on setting up Messenger webhooks](https://developers.facebook.com/docs/messenger-platform/webhooks). Meta App Review gates live sending Sending only works once Meta has approved your Meta App’s Messenger permissions for this Page. Until App Review is complete, the webhook can verify successfully, but messages won’t go through. Confirm with your developer team that Messenger permissions for this Page are approved in Meta App Review. ## What’s next After setup, you can send Facebook Messenger messages in Customer Journeys and one-time messages. [Learn more about Facebook Messenger messages ](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/) --- # KakaoTalk platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration/ Learn how to configure the KakaoTalk platform to send messages to users in South Korea and other regions. --- Pushwoosh enables you to send messages to users via KakaoTalk. By integrating KakaoTalk, you can extend your reach to users in South Korea and other regions where this messaging platform is dominant. ## Prerequisites Before configuring KakaoTalk, ensure the following requirements are met: * **Pushwoosh account**: A project where you want to enable KakaoTalk. * **A registered Kakao Business account** * **User Code and Secret Key**: Credentials from the Kakao Business Center required to connect your KakaoTalk account to Pushwoosh and authorize message sending. ## Setting up the KakaoTalk configuration To set up the KakaoTalk configuration, follow these steps: 1. Navigate to **Settings > Configure Platforms**. Scroll down the list of platforms to find **KakaoTalk** and click **Connect**. ![KakaoTalk platform in the Configure Platforms list](https://docs.pushwoosh.com/configure-project-kakao-configuration-1.webp) 2. In the **KakaoTalk notifications** modal, enter your Kakao Business credentials: * **USER CODE**: Your unique User Code from the Kakao Business Center. * **SECRET KEY**: Your corresponding Secret Key. These credentials verify your identity and authorize Pushwoosh to send messages on your behalf. ![KakaoTalk configuration modal with User Code and Secret Key fields](https://docs.pushwoosh.com/configure-project-kakao-configuration-2.webp) 3. Click **Apply** to save your configuration. ## What’s next KakaoTalk uses a template-based messaging system. Before you can send campaigns or transactional messages, you need to: 1. Create a message template. 2. Submit it for approval by the Kakao team. [Learn how to create and manage KakaoTalk message templates](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/). Once your templates are approved, you can send KakaoTalk messages using: * **API**: Send messages programmatically via the [Kakao API endpoint](https://docs.pushwoosh.com/developer/api-reference/kakao/). * **Journey**: Create automated messaging flows in the [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/kakao/send-kakao-messages/) builder. --- # SMS platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/ Configure the SMS channel in Pushwoosh by connecting your SMS provider, then use SMS in campaigns and Customer Journeys. --- Pushwoosh enables you to send SMS messages to users by connecting your own SMS provider. Once configured, you can send [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms) in one-time campaigns and Customer Journeys. ## Prerequisites Before configuring SMS, ensure the following requirements are met: * **Pushwoosh project**: A project where you want to enable the SMS channel. * **SMS provider account**: An account with one of the [supported SMS providers](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/#supported-sms-providers). * **Provider credentials**: The credentials required by your provider, such as account identifiers, API keys, a sender ID, or a phone number. ## Setting up the SMS configuration To set up the SMS configuration, follow these steps: 1. Go to **Settings → Configure Platforms**, select **SMS**, and click **Connect**. ![SMS platform in the Configure Platforms list](https://docs.pushwoosh.com/configure-project-sms-configuration-2.webp) 2. Choose your **SMS provider** from the list of [supported SMS providers](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/#supported-sms-providers). 3. Enter the provider credentials. The fields, their labels, and any hint text depend on the provider you select. Required fields are marked with an asterisk, and sensitive fields such as API tokens are masked as you type. For TextMe.co.il, see [TextMe.co.il credentials](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/#textmecoil-credentials). ![SMS provider credential fields in the configuration form](https://docs.pushwoosh.com/configure-project-sms-configuration-4.webp) 4. Click **Apply** to save the credentials. 5. To confirm the credentials work, enter a phone number in international format (for example, +14155550100) and a short message, then click **Send test**. If the message doesn’t arrive, recheck the credentials against your provider’s dashboard. An invalid API token, account ID, or sender ID is the most common cause. ![Sending a test SMS message to verify the configured provider](https://docs.pushwoosh.com/configure-project-sms-configuration-3.webp) ## Supported SMS providers Pushwoosh supports the following SMS providers: * [Twilio](https://www.twilio.com/): can also send RCS messages through your own Twilio account with a Messaging Service and a carrier-approved RCS sender. The **Messaging Service SID** field is optional for SMS but required for RCS. For the requirements and the setup, see [RCS messages through Twilio](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/). * [Amazon SNS](https://aws.amazon.com/sns/) * [SMSGlobal](https://www.smsglobal.com/) * [SMSAla](https://smsala.com/) * [CapitoleMobile](https://www.capitolemobile.com/) * [Mobipace](https://www.mobipace.com/) * [SMSCenter](https://smscentre.com/) * [SMSTraffic](https://www.smstraffic.ru/) * [TextMe.co.il](https://textme.co.il/) * [AbleMobile](https://ablemobile.com/) * [MStat](https://campaign.omni-messaging.com/): the original MStat connection, with its own fixed credential fields. * [MStat (Omni Messaging)](https://campaign.omni-messaging.com/): a separate connection to the same provider, with credential fields, labels, and hint text specific to this integration. * [SMSAPI.ro](https://www.smsapi.ro/) * [SMPP](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/smpp-transport/): connect your own bind to an operator or aggregator instead of a routed provider — set up by your Pushwoosh account team, not through **Configure Platforms**. To get your credentials, refer to your SMS provider’s documentation. Provider availability may depend on your account and region. ### TextMe.co.il credentials TextMe.co.il needs a **Username**, a **From number**, and either an API token or a password: * **API token:** create the token in the TextMe web console, as described in the [TextMe authentication guide](https://docs.textme.co.il/guide/authentication#where-tokens-come-from), and paste it into the **API token** field. TextMe tokens are valid for 730 days. Create a new token and update the field before the old one expires, otherwise SMS sending through TextMe stops. * **Password:** configurations that already authenticate with the TextMe password keep working. For a new connection, use an API token. If both are filled in, Pushwoosh authenticates with the API token. ## Provider webhooks Developer assistance needed You’ll need your developer team’s help to register provider webhooks. Share the guide below with them to get started. After you save credentials in **Configure Platforms**, check whether your SMS provider needs webhook setup. Some providers configure callbacks automatically, others require URLs in the provider dashboard, and some do not support webhooks at all. [SMS provider webhooks ](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/sms-provider-webhooks/) ## What’s next After setup, you can send SMS in one-time campaigns and Customer Journeys. [Learn more about SMS ](https://docs.pushwoosh.com/product/messaging-channels/sms/) --- # Telegram platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/ Learn how to configure Telegram in Pushwoosh by creating a bot with BotFather, adding the API token, and verifying the connection. --- Connect your Telegram bot to Pushwoosh to run Telegram as part of your omnichannel journeys from one place and track delivery results in Journey analytics. ## Prerequisites Before configuring Telegram, ensure the following requirements are met: * **Telegram account:** Use an active Telegram account that can access [@BotFather](https://t.me/BotFather). * **Telegram bot:** Create a Telegram bot in BotFather. ## Copy your Telegram bot API token To get an API token for an existing bot: 1. In Telegram, open **@BotFather**. 2. Send `/mybots` and select your bot. 3. Tap **API Token** and copy the token. If you do not have a bot, create one in **@BotFather** by following [the official Telegram guide](https://core.telegram.org/bots/features#botfather). ## Set up the Telegram configuration in Pushwoosh To set up the Telegram configuration, follow these steps: 1. Navigate to **Settings > Configure platforms** and open **Telegram**. 2. If you already have a bot, click **Add token**. If not, click **Create a bot**. When you click **Create a bot**, Pushwoosh opens instructions for creating the bot in Telegram via **@BotFather**. Complete bot creation in Telegram, then return to Pushwoosh and connect the bot token. ![Telegram integration screen in Configure platforms with Add token and instructions for BotFather.](https://docs.pushwoosh.com/configure-project-telegram-configuration-1.webp) 3. In the **Connect Telegram bot** dialog, enter the **API token** you copied from BotFather. [Learn how to get the token](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/#copy-your-telegram-bot-api-token). ![Connect Telegram bot dialog with Display name, API token field, Check token, Cancel, and Connect.](https://docs.pushwoosh.com/configure-project-telegram-configuration-3.webp) 4. Click **Check token** to verify the token. ![Connect Telegram bot dialog with Display name, API token field, Check token, Cancel, and Connect.](https://docs.pushwoosh.com/configure-project-telegram-configuration-2.webp) 5. Click **Connect** to save. To discard changes, click **Cancel**. ## Manage Telegram bot tokens Caution Each project can use **one** Telegram bot API token at a time. To use a different bot, replace the token in **Configure platforms** or use another project. If you change the bot API token, active Telegram Journeys will be paused. ### Add a new token 1. In **Telegram** settings, click **Add token**. 2. In the **Connect Telegram bot** dialog, enter the new **API token**. 3. Click **Check token**. 4. Click **Connect**. ### Delete a token 1. Click the **three-dot menu** next to the bot token. 2. Click **Delete**. Deleting a token pauses all active Telegram journeys. ## Send Telegram messages After setup, you can send Telegram messages in **Customer Journey Builder**. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/) --- # Viber platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/viber-configuration/ Learn how to configure Viber in Pushwoosh by adding your Rakuten Viber auth token in Configure platforms to enable messaging. --- Pushwoosh enables you to send Viber messages to users as part of your omnichannel campaigns. Connect your Viber account to Pushwoosh to use Viber in Customer Journey. ## Prerequisites Before configuring Viber, ensure the following requirements are met: * **Pushwoosh account:** A project where you want to enable Viber. * **Auth token:** Your Rakuten Viber authentication token required to connect Viber to Pushwoosh. [Learn more](https://developers.viber.com/docs/api/rest-bot-api/). ## Set up the Viber configuration To set up the Viber configuration, follow these steps: 1. Navigate to **Settings → Configure platforms**. Scroll down the list of platforms to find **Viber** and click **Connect**. ![Viber platform in Configure platforms with Not configured status and Connect button.](https://docs.pushwoosh.com/configure-project-viber-configuration-1.webp) 2. In the **Viber notifications** dialog, enter your **AUTH TOKEN** from Rakuten Viber. ![Viber notifications dialog with AUTH TOKEN field, Cancel button, and Apply button.](https://docs.pushwoosh.com/configure-project-viber-configuration-2.webp) 3. Click **Apply** to save your configuration. To discard changes, click **Cancel**. ## What’s next After setup, you can send Viber messages in Customer Journeys. [Learn more about Viber messages ](https://docs.pushwoosh.com/product/messaging-channels/viber/) --- # WhatsApp platform configuration **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/ Learn how to connect WhatsApp in Pushwoosh, monitor sender status and quality rating, and set up an automatic welcome message. --- Pushwoosh enables you to send personalized messages to customers via WhatsApp. Follow this guide to set up the WhatsApp platform. ## Prerequisites Before beginning the setup, ensure the following requirements are met: * **Access to Facebook Business Manager**: You need a Facebook account with permissions to manage the WhatsApp Business Account within Facebook Business Manager. * **Payment method for WhatsApp**: Meta charges for each message sent via WhatsApp. Pushwoosh does not handle billing, so ensure your payment information is configured in the **Payment settings** of your WhatsApp account. ## Setting up the WhatsApp configuration To set up the WhatsApp configuration, follow these steps: 1. Navigate to **Campaigns > WhatsApp messages**. 2. Click **Login with Facebook** to start the configuration process. ![Start WhatsApp setup from Campaigns with the Login with Facebook button highlighted](https://docs.pushwoosh.com/configure-project-whatsapp-configuration.webp) Alternatively, navigate to **Settings > Configure Platforms**. Locate **WhatsApp** in the list of available platforms and click **Login with Facebook** next to the WhatsApp option. ![Settings menu showing Configure Platforms option and WhatsApp platform with Login with Facebook button.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-1.webp) 3. Follow the on-screen prompts to log in with your Facebook Business account. ![Facebook login screen for Pushwoosh WhatsApp integration with account continue options](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-2.webp) 4. To proceed with onboarding, click **Continue as [Your Name]** to grant Pushwoosh access to your Facebook profile information, which includes your name and profile picture. This does not allow Pushwoosh to post on Facebook on your behalf. ![Facebook profile confirmation screen showing user information before granting Pushwoosh access.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-3.webp) If the displayed account is incorrect, click **Log in to another account** to switch to the appropriate Facebook account with Business Manager access. Ensure you are using an existing Facebook account, as new accounts cannot be approved for Business Manager. ## Connect your account to Pushwoosh and enter your business information 1. After logging in, you’ll see a screen titled **Connect your account to Pushwoosh**. This screen outlines the permissions Pushwoosh will need to send WhatsApp messages on your behalf. ![Pushwoosh permissions request screen showing required access for WhatsApp message sending.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-4.webp) Pushwoosh will request permission to: * Add or link phone numbers to your WhatsApp Business Account. * Create and manage message templates. * Send and receive messages on your behalf. * Assign users to your account. * Access your metrics. Carefully review each permission and the applicable terms of service, including the **WhatsApp Business Terms of Service**, **Meta Terms of Service**, and **Business Tools Terms of Service**. Review **Pushwoosh’s Privacy Policy and Terms** for details on how your data is managed. Once you’re ready, click **Get started** to continue. 2. To complete the setup, provide your business details. These details are used internally and will not be visible to customers on WhatsApp. In the **Business portfolio** section, either select an existing portfolio or create a new one. Then, fill in the required business details: * **Business name** * **Business website** * **Country** * **Address** (optional) ![Business portfolio section showing fields for business name, website, country, and optional address.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-5.webp) 3. In the **Choose a WhatsApp Business Account** section, select an existing account from the dropdown menu. This account will be linked to your business portfolio in Pushwoosh. If you don’t have a WhatsApp Business account, select **Create a WhatsApp Business Account**. 4. In the **Create or Select a WhatsApp Business Profile** section, choose an existing profile or create a new one to represent your business on WhatsApp. ![WhatsApp Business Account selection dropdown and Business Profile creation options.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-6.webp) 5. Add the phone number your customers will receive messages from, and verify it with the code Meta sends. Caution Don’t skip the phone number. If you finish the setup without one, WhatsApp isn’t connected in Pushwoosh and you can’t send messages. Pushwoosh shows **Your WhatsApp Business account is connected, but no phone number was added**. To fix it, start again from step 1 of [Setting up the WhatsApp configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#setting-up-the-whatsapp-configuration) and add a phone number. 6. Review the permissions Pushwoosh will have to manage your WhatsApp account. These include: * Managing your selected WhatsApp account. * Accessing and managing conversations via your selected WhatsApp account. 7. Click **Continue**. 8. Next, you will see a confirmation screen indicating that the WhatsApp connection steps are done. Do not close the window yet. Facebook will then review your business to ensure compliance with their Commerce Policy. This review typically occurs in the background, and you will only be contacted if there is an issue, usually within 24 hours. ![WhatsApp setup completion confirmation screen with next steps for Facebook business review.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-7.webp) 9. Review the conversation quotas Meta shows for this sender: * **Business-initiated conversations:** conversations your business starts by messaging a customer first. You can initiate up to 250 conversations per phone number within a rolling 24-hour period. * **Customer-initiated conversations:** conversations where the customer messages your business first. There are no limits on these, so you can respond without restriction. 10. Click **Finish** to close the setup wizard. After setup, the live cap Meta allows for this sender appears on the **Message limit** tile under [Check your sender status and quality rating](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#check-your-sender-status-and-quality-rating). Once configured and after the review process is complete, the status and basic information of your WhatsApp integration will appear in the list of configured platforms in Pushwoosh, enabling you to [start interacting with customers on WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages). ## Check your sender status and quality rating After you connect a WhatsApp Business Account, go to **Settings > Configure Platforms > WhatsApp**. The management screen opens instead of the login wizard. Use it to monitor your sender and reconnect it if Meta reports a problem. ![WhatsApp management screen with sender status, WABA ID, quality rating, message limit, and welcome message](https://docs.pushwoosh.com/whatsapp-sender-status-1.webp) The page header shows two independent statuses, one from Pushwoosh and one from Meta: * **Enabled** / **Disabled**: whether you turned the platform on or off in Pushwoosh. * **Sender number**: the WhatsApp phone number linked to this account. Next to it, **Connected** or **Sending blocked** shows whether Meta currently accepts messages from this sender. This badge only appears while the platform is enabled. From the same screen you can: * Click **Re-login** to repeat the Facebook connection steps above, for example if Meta reports a connection problem. * Open the three-dot menu (**⋮**) and choose **Disable platform** (or **Enable platform** if it’s already disabled). Disabling disconnects the channel, so campaigns and automations can’t send WhatsApp messages until you enable it again. ### Sender details Below the header, check these four tiles for your sender’s current details: * **WABA ID**: your WhatsApp Business Account ID. Click the copy icon to copy it. * **Name visible to customers**: the business name Meta displays to WhatsApp users you message. * **Message limit**: the maximum number of unique customers your sender can message in a rolling 24-hour period. For example, `1 000` or `100 000`, or **Unlimited** once Meta lifts the cap. * **Quality rating**: how Meta rates your sender based on recent recipient feedback. Meta reports quality rating as one of the following levels: | Rating | Meaning | | ------- | ------------------------------------------------------------------------------------- | | High | No significant negative feedback on recent messages. | | Medium | Meta has flagged some negative feedback. Review your messaging content and frequency. | | Low | Meta has flagged significant negative feedback. Your message limit may drop. | | Unknown | Meta hasn’t returned a rating yet for this sender. | Tip If your rating drops to **Medium** or **Low**, reduce sending frequency and review recent message content. Meta ties your message limit to this rating. ### How to fix a blocked or limited sender When Meta restricts sending, check the banner above the sender details: * A warning banner means Meta has temporarily reduced your sending capacity. Messages still go through, but at a lower rate. ![WhatsApp warning banner for limited sending when business verification has not passed](https://docs.pushwoosh.com/whatsapp-sender-status-3.webp) * An error banner means Meta has stopped your sender from sending entirely. The **Sender number** badge shows **Sending blocked** instead of **Connected**. ![WhatsApp error banner for blocked sending with payment method reason, and Sending blocked badge](https://docs.pushwoosh.com/whatsapp-sender-status-2.webp) To restore sending: 1. Open WhatsApp Manager and resolve the issue described by the banner. An error code appears when Meta provides one. 2. Reload **Settings > Configure Platforms > WhatsApp**. 3. Confirm the banner is gone and the **Sender number** badge shows **Connected**. Caution If you see a configuration error banner instead of the tiles, for example when the Facebook connection expired or Meta no longer returns your sender number, click **Re-login** on that banner to reconnect your account. ## Accept Meta’s Marketing Messages API terms To keep sending WhatsApp marketing templates from October 13, 2027, accept Meta’s Marketing Messages API terms. From that date, Meta sends marketing templates only for businesses that have accepted these terms. Meta tracks acceptance for each WhatsApp Business Account separately, so if you’ve connected more than one, accept the terms for each of them. Utility, authentication, and service messages are not affected. You don’t need to change your templates or campaigns, because Pushwoosh sends marketing templates through the Marketing Messages API automatically. If you connect WhatsApp to Pushwoosh now, you accept these terms in the Meta connection window during [setup](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#connect-your-account-to-pushwoosh-and-enter-your-business-information). The steps below are for WhatsApp Business Accounts connected earlier, and for any account where Pushwoosh still shows a warning. ### What happens if you don’t accept the terms Before October 13, 2027, your marketing templates are sent as before. From that date, Meta stops sending marketing templates from a WhatsApp Business Account that hasn’t accepted the terms, so campaigns and Journeys that use marketing templates won’t reach customers. Other message types keep working. ### Where Pushwoosh shows that the terms aren’t accepted While the terms aren’t accepted, Pushwoosh shows a warning in these places: * **Settings > Configure Platforms:** a warning icon on the **WhatsApp** row, with the text **Accept Meta’s Marketing Messages API terms to keep sending marketing templates after October 13, 2027**. * **Settings > Configure Platforms > WhatsApp:** a warning banner titled **Accept Meta’s new terms for marketing messages**, with an **Open WhatsApp Manager** link. * **[Notification center](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#notification-center-top-right-corner):** a warning titled **[Pushwoosh Alert] Accept Meta’s new terms for WhatsApp marketing messages**, with an **Open WhatsApp Manager** link. ### How to accept the terms Only someone with full control of the Meta business portfolio that owns the connected WhatsApp Business Account can accept the terms. If you don’t have that access, ask your portfolio admin. 1. In the warning banner on **Settings > Configure Platforms > WhatsApp**, click **Open WhatsApp Manager**, or open WhatsApp Manager in Meta Business Suite directly. 2. Go to **Overview**. 3. In **Alerts**, find the alert about the Marketing Messages API and accept the terms. Caution If **Alerts** has no item about the Marketing Messages API, check that you’re signed in to the business portfolio that owns this WhatsApp Business Account and that you have full control of it. If the item is still missing, Meta hasn’t offered the terms to this account yet. Check again later, as the warnings in Pushwoosh stay until the terms are accepted. After you accept, Meta needs some time to finish setting up your account. The warnings in Pushwoosh disappear after Meta reports the setup as complete. The banner on the WhatsApp screen can take up to an hour to update, and the Notification center warning is cleared within a day. ## Set up an automatic welcome message You can set up an automatic first reply for customers who message your WhatsApp number for the first time. Each customer receives it only once. To turn on a welcome message for the first time: 1. Open **Settings > Configure Platforms > WhatsApp**. 2. On the **Welcome message** card, click **Enable**. ![Welcome message card showing Disabled badge, Enable button, and Journey duplicate-message note.](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-9.webp) 3. At the top of the editor, pick the language to write and send this version of the message in. If the language you need isn’t listed yet, click **+** to add it: each language keeps its own message text, separate from the others. ![Language dropdown set to Default language with plus button to add another language](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-12.webp) * **Send the same language to everyone:** select that language, write its text, and click **Save** while it’s still selected. Every customer gets that text, no matter their own **Language** tag. If you leave the text empty, customers get the **Default language** text instead. * **Let Pushwoosh pick per customer:** write text for every language you want to cover, then select **Default language** before you click **Save**. Pushwoosh matches each customer’s own **Language** tag to a language you’ve written, trying an exact match first, then the base language ignoring region (`en-US` matches `en`), then **Default language**’s text if nothing matches. Give **Default language** its own text too. Otherwise, a customer who doesn’t match any language you’ve written gets no greeting at all. Tip For example: you’ve written **Default language** in English, plus French and German. Select **Default language** and save: `fr` gets French, `de-DE` gets German (region ignored), `es` (no Spanish written) gets English. Select German and save instead: everyone gets German, including `fr` and `es`. Now say **Default language** has no text of its own instead: a customer tagged `es` (no Spanish, no **Default language**) gets nothing. 4. Use the toolbar under the field to apply bold, italic, strikethrough, or text color, or to insert an emoji. 5. To insert a Tag or the customer’s User ID: * Place the cursor where the value should appear. * Click **Personalization** above the message field. * Under **Tag**, choose a Tag or **User ID**. * In the next dropdown, choose a format modifier (for example, **CapitalizeFirst**). * Optionally enter a **Default tag value** for customers who don’t have that Tag set. * Click **Insert**. ![Personalization popover with Tag firstName, CapitalizeFirst modifier, Default tag value dear customer, and Insert](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-11.webp) 6. Optionally, add an image in **Attachment URL**: paste a URL, or click the paperclip icon to pick a file from the [Media storage](https://docs.pushwoosh.com/product/content/media-store/). Unlike the text, this image is shared across every language you’ve added. ![Attachment URL field with paperclip icon to open Media storage for the welcome message image](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-13.webp) 7. Click **Save**. ![Welcome message editor with language selector, Personalization button, text, formatting toolbar, attachment URL, Save, and WhatsApp preview](https://docs.pushwoosh.com/configure-project-whatsapp-configuration-10.webp) ### Manage the welcome message Use the same **Welcome message** card to turn the reply on or off, edit it, check statistics, or delete it. #### When the welcome message is Enabled **Enabled** means the welcome message is saved and sending automatic replies. * Click **Disable** to stop automatic replies without deleting the saved text. * Open the three-dot menu and choose **Edit message** to change the text. * Open the three-dot menu and choose **Delete** to remove the saved welcome message entirely (not just turn it off). * Click **View detailed statistics** for the current campaign’s numbers. #### When the welcome message is Disabled **Disabled** covers two different situations: **No message yet** The message was never set up, or was removed with **Delete**. Only **Enable** is available. Click it to create the message. **Message saved but turned off** The message exists but isn’t sending. * Click **Enable** to turn automatic replies back on without rewriting the text. * Open the three-dot menu and choose **Delete** to remove the saved message, or **View detailed statistics** for the current campaign’s numbers. #### After you delete the welcome message **Delete** removes the welcome message from this card, but Campaigns keeps the statistics for messages already sent. To send welcome messages again, [set up a new welcome message](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#set-up-an-automatic-welcome-message). This starts a new campaign, so **View detailed statistics** then opens the new campaign, not the deleted one. To check the statistics from before the deletion, go to **Statistics > [Aggregated messages](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages)** and open the older **WhatsApp welcome message** entry. Both the old and the new welcome-message campaigns share this same name, so tell them apart by creation date. ### Things to know * The welcome message depends on Meta delivery status. If your sender is currently blocked, the welcome message won’t be delivered until Meta restores sending. See [How to fix a blocked or limited sender](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#how-to-fix-a-blocked-or-limited-sender). * If a Journey campaign is also triggered by incoming WhatsApp messages, a customer’s first message can trigger both the welcome message and the Journey, so they may receive two messages back to back. Adjust your Journey trigger or disable the welcome message to avoid this. --- # Create your project **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/ Step-by-step guide to creating and configuring your first Pushwoosh project for mobile, web, SMS, WhatsApp, and email marketing automation. --- All marketing automation in Pushwoosh is conducted within projects. Usually, one project is enough to work with one app on multiple platforms. If you have several different apps, you can create a separate project for each app. Creating a project takes one form. Right after it, Pushwoosh offers to set up your brand and your email footer, so the first emails and in-app messages you build already carry your colors, logo, and legal details. ## Create your first project After you sign up and answer the onboarding questions, Pushwoosh opens the **Just one final step left** form. Fill it in the same way as when [creating any other project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/#create-a-new-project), then click **Save and continue** to open the [setup steps](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/#set-up-your-brand-and-email-footer). ## Create a new project 1. Go to [**Projects**](https://app.pushwoosh.com/projects) and click **Create project**. ![Projects list toolbar with the Create project button highlighted next to All projects report.](https://docs.pushwoosh.com/start-with-your-project-create-your-project-10.webp) 2. Enter the **Project name**. 3. Optionally, click **Browse icon…** and upload a PNG, JPG, or GIF icon of up to 0.5 MB. The icon is shown next to the app name in push notifications. 4. Keep **Allow all API tokens to communicate with this project** selected to let your existing API tokens send messages and modify device data in this project. Clear it if you want to grant access to each token separately. 5. Click **Create**. ![Create project form with Project name set to Acme Shop, the Project icon with Browse icon button, the selected Allow all API tokens checkbox, and the Create button.](https://docs.pushwoosh.com/start-with-your-project-create-your-project-11.webp) Pushwoosh creates the project and opens the setup steps. ## Set up your brand and email footer Right after you create a project, Pushwoosh walks you through up to two steps: * [Brand](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/#set-up-your-brand), shown for every new project. * [Email footer](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/#add-your-email-footer), shown only when the project is likely to send email. You can skip either step. The setup doesn’t offer a skipped step again, and each step below says where to set it up later. If you leave before finishing, Pushwoosh returns you to the first unfinished step the next time you open the project’s overview. When all steps are done or skipped, the project overview opens. ### Set up your brand The brand you set here is saved to the project’s [Brand book](https://docs.pushwoosh.com/product/content/brand-book/), so the email, in-app, and cloud page editors start from your colors, fonts, logo, and tone of voice. 1. Under **Build from your website**, enter your site’s domain and click **Scan site**. Pushwoosh shows the colors, fonts, logo, and tone of voice it found on the site. If the scan fails, Pushwoosh shows the error below **Derive brand**. Fix the domain and scan again, or use your own materials as described next. To build the brand from your own materials, describe the brand in **Brand voice**, click **Attach files** to add a logo image or brand documents, and click **Derive brand**. 2. Check **Brand voice** and edit it if the text doesn’t sound like your brand. 3. Click **Apply brand** to save the brand to the Brand book. ![Set up your brand step after scanning pushwoosh.com, with color swatches, Brand voice text, and the Apply brand button](https://docs.pushwoosh.com/start-with-your-project-create-your-project-12.webp) If you click **Skip for now**, nothing is saved. You can [build the brand from your website](https://docs.pushwoosh.com/product/content/brand-book/#build-from-your-website) in Brand book later. ### Add your email footer Every email needs the sender’s legal details in its footer. This step saves them as the project’s default footer, which new emails start from. The step appears only if you have the **Domain and email verification** [permission](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#available-permissions) and at least one of these is true: * Your account is on the free Email plan. * The project already has an email sending domain. * You’re creating your first project and chose **Email** among the channels in the onboarding questions. If you scanned your site in the [brand step](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/#set-up-your-brand), the fields are already filled from it, and the step says so. A scan fills only what the site shows, so check every field and fill in any that stay empty before saving. 1. Enter the **Company name**. It’s required. 2. Enter the **Address**. It’s required. 3. Optionally, add the **Privacy policy link**, **Terms link**, and **Contact link**. Each link must start with `https://`, `http://`, or `mailto:`. 4. Click **Save footer**. ![Add your email footer step prefilled from a site scan, with an empty Address field and the Save footer button](https://docs.pushwoosh.com/start-with-your-project-create-your-project-13.webp) If you click **Skip for now**, nothing is saved. You can set the footer later in any email in the Drag & drop editor. Fill in the footer and click [**Save as project default**](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/#load-or-save-a-project-footer). The saved footer shows the company name, the address on one line, a copyright line (© and the current year followed by the company name), and the links you added. New emails you create in the Drag & drop editor get this footer automatically, unless they already have a footer of their own, for example from the template you started with. Emails created before you saved the footer keep their own. To switch one to the project footer, open its footer settings and click [**Load from project**](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/configure-email-styling-and-footer/#load-or-save-a-project-footer). ## Project configuration Developer assistance needed You’ll need your developer team’s help to configure your Pushwoosh project. Share the guides below with them to get started. ### Mobile configuration #### iOS configuration * [iOS certificate-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration) * [iOS token-based configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-token-based-configuration) * [Apple Wallet pass configuration for iOS](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) #### Android Firebase configuration * [Android Firebase configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * [Google Wallet pass configuration for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) #### Huawei configuration * [Huawei configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/huawei-configuration) ### Web configuration * Configure web platforms using [these instructions](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/) ### Email configuration * [Email configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification) ### SMS configuration Follow [this guide](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration) to set up the SMS platform. ### WhatsApp configuration Follow [this guide](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) to set up the WhatsApp platform. ### Apple Wallet configuration Follow [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) to set up Apple Wallet passes. ### Google Wallet configuration Follow [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) to set up Google Wallet passes. ## Project settings To access project settings, go to the **Configure platforms** section and click **Edit project**: ![Configure platforms page showing the Edit project button to access project settings and configuration options.](https://docs.pushwoosh.com/start-with-your-project-create-your-project-5.webp) The **Edit project** dialog lets you change the following: * **Title:** Update the project name in the **TITLE** field. * **Icon:** Click **Browse…** next to the current icon to upload a new image (preferably no larger than 512×512 px and 1 MB). The icon is shown next to the app name in push notifications. * **Bit.ly:** Check **I want to add my bit.ly account**, then enter your bit.ly access token in the **ACCESS TOKEN** field. Using your own account ensures links in your messages are shortened and sent within your shortener limits. * **Email Notification Settings:** Check **I want to receive Daily Application Performance Emails** to get daily performance reports. Reports are sent for apps with 50 or more subscribers. The recipient email is shown below the checkbox. You can unsubscribe at any time. Click **Save** to apply your changes or **Cancel** to discard them. ![Edit project dialog with TITLE, Icon with Browse button, bit.ly checkbox and Access token field, and Email Notification Settings with Save and Cancel buttons.](https://docs.pushwoosh.com/start-with-your-project-create-your-project-6.webp) --- # Ways to register users **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/ Register users via SDK or API, CSV import, or an existing push subscriber base, and sync data with third-party integrations. --- To effectively target and engage users through Pushwoosh, it’s crucial to ensure that user devices are properly registered and your data is synchronized. Pushwoosh offers several ways to register users, depending on whether you’re integrating a new app or bringing over an existing audience. ## New users ### Register via SDK or API Developer assistance needed Share the setup guide with your development team: [SDK](https://docs.pushwoosh.com/developer/first-steps/subscribe-users/#pushwoosh-sdk), [Web Push SDK](https://docs.pushwoosh.com/developer/first-steps/subscribe-users/#web), or [API](https://docs.pushwoosh.com/developer/first-steps/subscribe-users/#pushwoosh-api). For mobile apps, Pushwoosh SDK integration is the recommended way to register devices and synchronize data automatically. It calls the registerDevice API method internally and can also register phone numbers for SMS and WhatsApp notifications. For websites, integrate the [Web Push SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30), then set up a custom or default [Subscription Prompt](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt) so visitors can opt in to push notifications. [](https://docs.pushwoosh.com/start-with-your-project-subscribe-users-1.webm "Website showing subscription prompt for web push notifications with browser permission dialog.") [Learn more about Pushwoosh SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview) For more advanced registration and sync scenarios, use the Pushwoosh API instead. [Learn more ](https://docs.pushwoosh.com/developer/first-steps/subscribe-users/#pushwoosh-api/) ### Use a third-party integration Connect a platform you already use to sync users and data with Pushwoosh automatically. [Learn more ](https://docs.pushwoosh.com/product/integrations/) ## Importing your existing user base If you’re working with large sets of pre-existing data, you can import it into Pushwoosh instead of collecting it from scratch. ### Mobile push subscribers If you’re migrating from another push provider, import your existing push subscriber base from a CSV file. Tokens from your previous provider keep working as long as the app still uses the same APNs key, Firebase project, or HMS credentials, so users don’t need to reinstall the app or grant permission again. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/import-push-subscribers/) For migrations this self-serve import doesn’t cover, contact us via the [Contact Us form](https://help.pushwoosh.com/hc/en-us/requests/new) and provide a sample of your user base. Our team will review the data format and assess whether the migration is feasible. ### Web push subscribers Web users cannot be imported due to technical limitations. Different push providers use varying payload formats and platform configurations, making importing web users ineffective. As a result, the imported user base would either be invalidated or receive incorrect or blank content. However, web users who have already given permission to receive push notifications for your website can be resubscribed. [Learn more ](https://help.pushwoosh.com/hc/en-us/articles/360018326111-Migrate-your-userbase-to-Pushwoosh/?utm_content=docs&utm_term=post&utm_campaign=migrate-your-userbase-to-pushwoosh) ### Email, SMS, WhatsApp, and Viber contacts Import contacts from a CSV file to register email, SMS, WhatsApp, and Viber subscribers in one pass. [Learn more ](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) --- # Import contacts **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/ Upload one CSV file to register email, SMS, WhatsApp, and Viber subscribers at once by mapping each column to a channel. --- Register contacts for email, SMS, WhatsApp, and Viber from one CSV file. One phone number column can register a contact for both SMS and WhatsApp, so you don’t need a separate file per channel. ## Prepare your CSV file ### File format * The first row must contain column headers describing the data in each column. * Each subsequent row represents a single contact, with values corresponding to the headers. * Values must be separated by commas. * Save the file in UTF-8 encoding to ensure proper character display. * The maximum file size is 100 MB. ### Contact columns Include at least one of these columns, depending on the channels you want to register contacts for: * **Email:** the recipient’s email address. * **Phone number** (for SMS, WhatsApp, and Viber): in international format, starting with `+` and the country code, for example *+120XXXXX234*. Enter the plain number without a `whatsapp:` or `viber:` prefix. Pushwoosh adds the prefix when you map the column to that channel. If a phone number isn’t in a valid international format, it’s skipped for that channel. The rest of the row still imports. Note The column names in your CSV file don’t need to match these labels. During import, you map your existing column names to a channel in the wizard. For example, a column named *Mobile* can be mapped to the **SMS** or **WhatsApp** channel. ### Optional columns * **User ID:** a unique identifier for the user (e.g. name or email). Not required, but recommended so the contact’s channels share one user profile instead of several. * Any other column can be mapped to a system tag for segmentation and personalization, for example **Location**, **Birthday**, **Language**, or **Customer type**. ### Example CSV file ```csv Email,Mobile,User ID,Name,Location john.smith@email.com,+120XXXXX234,john.smith@email.com,John Smith,New York jane.smith@email.com,+131XXXXX890,jane.smith@email.com,Jane Smith,Los Angeles ``` ## Upload and map the CSV file 1. Go to **Audience → Import CSV → Import Contacts**. ![Import CSV hub in the Audience section showing the Import Contacts card alongside Update tag values, Import push subscribers, Import events, and Import subscription categories](https://docs.pushwoosh.com/import-contacts-1.webp) 2. Click **Upload CSV** and select your file. To replace it, click **Upload Another CSV**. Caution Pushwoosh checks the whole file when you upload it. A row that can’t be parsed (its column count doesn’t match the header, or a quoted value isn’t closed) gets the file rejected right away. A toast names the row and the reason, for example “Row 42: the number of values does not match the header.” Nothing is imported in this case. Fix that row and upload the file again. 3. Enter a **Segment name** for the imported contacts. 4. Under **Contact column in CSV**, select a column and, next to it, choose the **Channel** it registers (**Email**, **SMS**, **WhatsApp**, or **Viber**). Click **+ Add channel** to map another column, or to map the same column to an additional channel. 5. Under **UserId column in CSV**, select the column with unique user identifiers, if your file has one. 6. To include extra data for segmentation, select a **Column Name** from your CSV and assign it a **Tag**. Click **+ Add Column** to map more attributes. ![Map step of the Import Contacts wizard with one column mapped to Email, one Mobile column mapped to SMS, WhatsApp, and Viber, and a City column mapped to a tag](https://docs.pushwoosh.com/import-contacts-2.webp) 7. Click **Start Import**. Caution If Pushwoosh can’t check the email addresses when the import starts, the import stops before any row is imported. An **Import failed** toast says “Email addresses could not be checked because the validation service is unavailable. Try again later.” The wizard returns to the upload step, so upload the file and map the columns again a bit later. Pushwoosh checks addresses in batches of up to 1,000 rows. If the check becomes unavailable partway through a large file, the import also fails, but the rows processed before that point stay imported. After the import, the contacts are available for campaigns on the channels you mapped. They appear as a new segment in the [Segments list](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) and can also be accessed via [User explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). The completion screen shows two counts: * **Created:** channel registrations that are new to the app. * **Updated:** channel registrations that already existed, including when the same address or number re-registers under a different User ID or on a different device platform. Both count channel registrations, not contacts or CSV rows. A contact mapped to two channels, for example SMS and WhatsApp, adds 2. For an updated contact, only the tags mapped in step 6 change. Their values are overwritten with the CSV values. Other tags already set on the contact stay as they are. Note Older imports in the recent imports list show a single **Imported** count instead of **Created** and **Updated**. ## Rows skipped during import Pushwoosh checks every email address in the file by the same rules as an email subscription made through your website, the SDK, or the API. An address is skipped if it’s malformed, belongs to a disposable email service, or is on your account’s bounce list. The completion screen groups skipped rows by reason: * **Email address is not valid** * **Disposable email address** * **Email address is in the bounce list** * **Phone number is not valid** * **No contact address in the row:** every mapped column was empty A row can be partially imported and still show up in this breakdown. For example, the phone column imports, but the email address in the same row is skipped as disposable. Click **Download skipped rows** to get a CSV of those rows only. The file adds the skip reason as an extra column so you can fix the rows and re-upload them. Note Download the file before you leave the completion screen. If you open the same import later from the recent imports list on the Import CSV hub, you see the reason counts but no download link. ## Email addresses on domains without a mail server After the import, Pushwoosh checks in the background that the domain of each imported email address has a mail server. If the domain doesn’t exist or doesn’t accept mail, the address is unsubscribed from email. If the domain can’t be checked, for example because DNS doesn’t respond, the address stays subscribed. Pushwoosh doesn’t check whether a specific mailbox exists on a working domain. ## Rows the import failed to register Some rows pass the [skip checks](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/#rows-skipped-during-import) and are still rejected when Pushwoosh registers them. For example, a phone number in a valid format is rejected for SMS if it isn’t a mobile number. Pushwoosh counts rows like this as **Failed**, separately from Created, Updated, or Skipped. Failed rows aren’t broken down by reason and aren’t included in the skipped-rows download. To find them, compare your source CSV with the imported segment. Check the missing contacts, for example replace a landline number with a mobile one, and re-upload only those rows in a new import. If a row fails again and you can’t see why, contact Pushwoosh Support. --- # Set custom User IDs **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/ Learn why setting a custom User ID is essential for user tracking, personalization, and omnichannel messaging in Pushwoosh, and how to implement it. --- To enable seamless, personalized communication across all platforms and devices, it is essential to assign a custom User ID to each user. ## What is a User ID? User ID is a unique identifier assigned to each user. A single User ID can be linked to multiple devices, where each contact method (mobile app, email address, phone number) is [treated as a separate device](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/#understanding-how-pushwoosh-handles-devices). Linking all devices (contact methods) under one User ID allows you to reach users across channels and coordinate messaging in [omnichannel campaigns](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/). Note Each user can have up to **20 devices** linked to their User ID. If a new device is registered beyond this limit, the oldest inactive device is automatically removed. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/#device-limit-per-user) > For a detailed explanation of how User IDs work and how they relate to devices, see [this guide](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). ## Why you need to set a custom User ID By default, when your app is first launched, Pushwoosh creates a User ID equal to the device’s Hardware ID (HWID). This means each device is treated as a separate user. So if one person uses more than one device, like a phone and a tablet, Pushwoosh sees them as two separate users, and their data won’t be connected. Setting a custom User ID lets you link all devices and contact methods under one profile. This provides several important benefits: ### Accurate user counts for billing Setting a custom User ID helps avoid duplicate user records. This keeps your account data cleaner for billing and usage review. > [Learn more about Pushwoosh pricing](https://pushwoosh.com/pricing) If you don’t set a custom User ID, each device can be treated as a separate user, even if they belong to the same person. For example, if one user uses both a mobile phone and a tablet, they can be counted as two users. ### Consistent messaging across channels For a unified experience across push notifications, email, SMS, in-app, WhatsApp, and other channels, all user data must be linked under a single User ID. By setting a custom User ID, you ensure that Pushwoosh recognizes all contact methods (devices, emails, phone numbers) as belonging to the same user, which is essential for consistent and personalized omnichannel campaigns. > Learn more about [Omnichannel campaigns](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) ### Persistent user profiles A custom User ID allows Pushwoosh to maintain a complete, unified profile for each user. This profile includes: * All devices and contact info linked to the same user * Tags * Event history and campaign participation * Messages sent and opened You can view and manage this data in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/), helping you deliver more accurate targeting and better personalization. ## How to set a custom User ID Developer assistance needed Setting a custom User ID requires assistance from your development team. Please share the following technical guide with them for implementation steps: [Collecting User IDs](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) --- # Send a test communication **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/ Test your push, email, or in-app campaigns in Pushwoosh before launch to preview, verify, and ensure perfect delivery. --- Pushwoosh provides several ways to send a test communication, most of which require platform configuration. However, you can still test Pushwoosh functionality even without a configured platform by using the **Test email** feature. This feature allows you to preview how your email content will appear in recipients’ inboxes, ensuring everything looks perfect before finalizing your campaign. ## How to send a test email Tip Watch this tutorial video to learn how to test your emails in Pushwoosh, or refer to the guide below for more detailed instructions. [Youtube Video: How to test your emails in Pushwoosh](https://youtube.com/watch?v=a0sfyC-a8CY) Follow the steps below to send a test email: 1. Navigate to the email content you wish to test and select the option to send a test email. If you prefer not to create new content, you can choose from the pre-designed email templates provided by Pushwoosh. In the email editor, click **Test Email** to proceed. ![Email editor interface showing the Test Email button to send test communications.](https://docs.pushwoosh.com/shared-14.webp) In the window that opens: 1. In the **Email address** field, enter the email address where you want to receive the test email. Note If KYC **(Know Your Customer)** is not completed, you can still send test emails by verifying a test email address. [Learn more about how to configure a test email device](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices#test-email-device) ![Test email dialog showing the email address field and sample data inputs for dynamic content testing.](https://docs.pushwoosh.com/test-your-integration-README-1.webp) 2. If your email contains dynamic content (placeholders for personalized data), add sample values for testing purposes. For example: * **Age (integer)**: Enter a number to represent the age placeholder (e.g., `21`). * **FirstName (string)**: Enter a sample first name (e.g., `David`). These values will replace actual recipient data in the test email, allowing you to verify how the dynamic content appears. 3. Once you’ve filled in the necessary details, click **Send test email** to send the test message to the provided email address. ![Test email form showing sample data fields and the Send test email button to execute the test.](https://docs.pushwoosh.com/shared-34.webp) ## How to send a test email to a selected list of addresses You can test email messages by sending them to a specific list of addresses. Follow these steps: 1. Make sure you’ve configured the email platform and [verified the email address and domain name](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) from which you’re going to send emails. 2. Prepare email content using [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) or [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor). 3. Go to **Campaigns → One-time messages** → **Email**. 4. Select email content you want to send. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#configure-content). 5. In the **Audience step**, select **Send to email list**. Enter the email addresses you want to send your test communication to as a list, separated by commas. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#send-to-email-list). 6. In the **Audit** step, run a pre-send deliverability and compliance check on your email. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email). 7. In **Review**, check the preview and summaries for content, audience, and schedule. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#review-and-edit-your-content). 8. Finish [setting up your email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#schedule-email) and launch it or schedule it. ## How to preview your first push notification on a test device Push tests are intended to preview how push notifications look on a device before sending them to the app users. To test out your push notifications, first, register your device (with your app installed) as a Test Device. Developer assistance needed You’ll need assistance from your development team to register a push test device. [Please share this link with them for detailed guidance](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/) Once test devices are configured, use the **Test push notification** button in the [Push Content](https://docs.pushwoosh.com/product/content/push-presets) form. ![Push content editor interface showing the Test push notification button to preview push notifications on test devices.](https://docs.pushwoosh.com/test-your-integration-README-2.webp) ## How to test a journey with a test segment To accurately test a [journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), you must first create a test Segment that includes users meeting specific conditions. Steps to test a journey: 1. Create a **Segment** by either: * Uploading a CSV file with the necessary conditions. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import) * Asking your development team to assign a unique Tag value to your test devices by calling the [`/setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) API method. Then, [create a segment](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#createfilter) using this Tag value. 2. Navigate to **Campaigns → Create Campaign → Build a journey from scratch** or use one of the pre-built customer journey templates. 3. Add the [Audience-based entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) to the canvas and select the **Segment** you created. 4. Add a message element such as [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), [SMS](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/), or [email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/). 5. Select content from previously created content or create custom content. 6. Configure the remaining journey elements as needed. 7. Click **Launch Campaign** to start the test. --- # Test devices **URL:** https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/ Enhance campaign effectiveness by utilizing Pushwoosh's Test Devices to preview and adjust push notifications, emails, and SMS messages before they reach your audience. --- Tip Watch the video where our dedicated Customer Success team will guide you through the onboarding process at Pushwoosh, from setting up projects to sending your first communications. [Youtube Video: Onboarding process at Pushwoosh, from setting up projects to sending your first communications](https://youtube.com/watch?v=TUxcg73kAwQ) **Test Devices** enable you to preview how push notifications, emails, or SMS messages will appear on a device before sending them to your audience. This feature helps ensure that your messages are accurate and display correctly before launching campaigns to a larger audience. To use this feature, you must first register your device (push device, email, or phone number) as a Test Device. **Note:** Test devices are application-specific, meaning you’ll need to configure them separately for each application. To add a test device: 1. Navigate to the **Test Devices** section in your Pushwoosh account. ![Test Devices page with search, the Create test device button, and a paginated table of test devices](https://docs.pushwoosh.com/test-your-integration-test-devices-1.webp) 2. Click the **Create test device** dropdown button in the top-right corner. 3. Select the type of device you want to add: * **Push**: Add a mobile or web push device. * **Email**: Add an email address for testing email notifications. * **SMS**: Add a phone number for testing SMS messages. ![Create test device dropdown menu showing options to add Push, Email, or SMS test devices.](https://docs.pushwoosh.com/test-your-integration-test-devices-2.webp) ## Test push device You can add a push test device in one of three ways: * **QR code:** After a one-time setup in the app (deep links), anyone with the app installed can scan a QR code and register the device. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/#register-a-push-device-via-qr-code) * **From the app:** The app registers the current device itself, without scanning or manual entry. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/#registering-a-test-device-from-the-app) * **Manual:** Your developer gets the device Hardware ID (HWID) and enters it in **Create test device → Push**. Full steps are in the developer guide: [Adding test devices manually](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/#adding-test-devices-manually). Developer assistance needed for manual setup Manual HWID registration requires assistance from your development team. Please share [this guide](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/#adding-test-devices-manually) with them for proper implementation. ### Register a push device via QR code First, your developer needs to enable deep links for test devices in the app. This is a one-time setup, usually done when integrating the Pushwoosh SDK. See the [Implementation steps](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/#implementation) for details. After that, anyone with the app installed can register their device by scanning a QR code. No developer is needed for each new device, and this method doesn’t depend on the registration window described below. 1. Open the **Test Devices** section and click **Create test device → Push**. 2. Scan the QR code from the popup with any QR scanner app. See the [QR code / deep link method](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/#adding-test-devices-automatically) for details. ![Add Test Device dialog with Auto-configuration QR code on the left and Manual configuration fields for HWID on the right](https://docs.pushwoosh.com/test-your-integration-test-devices-6.webp) [Learn how to send a test push notification](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/#how-to-preview-your-first-push-notification-on-a-test-device) ### Registering a test device from the app Independently of the QR code method above, the Pushwoosh SDK can register the current device as a test device on its own: send the app to the background and bring it back to the foreground 6 times within 30 seconds. No scanning or manual entry is needed. To prevent unintentional registrations, this only works while the 1-hour registration window is open. 1. Open the **Test Devices** section. 2. In the page header, turn on **Add test devices from the app**. This opens the registration window for 1 hour. A countdown next to the toggle shows how much time is left. Note The toggle is only available to users with permission to modify the application. Without it, turning the toggle on fails with a “user cannot modify application” error. ![Test Devices page header with the Add test devices from the app toggle enabled, showing a countdown and the Create test device button](https://docs.pushwoosh.com/test-your-integration-test-devices-5.webp) 3. While the window is open, background and foreground the app 6 times within 30 seconds. The device appears in the **Test Devices** list. When the hour ends, the window closes automatically and the toggle turns off. You can also close the window earlier by turning the toggle off yourself. Note While the registration window is closed, devices can’t register themselves this way. The app’s registration call still returns success, but no device is added. Adding a device via QR code or manually with **Create test device** always works, regardless of the toggle. This setting is per application. ## Test email device You can also register an email address as a test device to test your email campaigns before launch. To add a test email device: 1. Go to **Settings → Test Devices** and click **Create test device → Email** 2. In the popup, fill in the required fields: * **Device Name**: Enter a descriptive name for the test device (e.g., “Test Email - Marketing”). * **Email**: Enter the email address where test messages will be sent. * **Description (Optional)**: Add any additional notes or details about the test device for reference. ![Email test device configuration form showing fields for device name, email address, and optional description.](https://docs.pushwoosh.com/test-your-integration-test-devices-3.webp) 3. Click **Save**. 4. After submitting the form, a verification code will be sent to the specified email address. 5. Enter the **verification code** you received into the **Verification Code** field shown in the dialog. Click **Verify** to confirm the device. [Learn how to send a test email](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration#how-to-send-a-test-email) ## Test SMS device You can register a test phone number to send and test SMS messages for formatting, delivery, and timing. 1. Go to **Settings → Test Devices** and click **Create test device → SMS** 2. Fill in the form: * **Device Name**: Enter a name to identify the test device (e.g., “Test SMS - QA Team”). * **Phone Number**: Enter the phone number where SMS test messages will be sent. Ensure the phone number is in the E.164 format (e.g., `+1234567890`). * **Description (Optional)**: Provide additional information about the device, such as its purpose or assigned user (e.g., “For testing transactional SMS messages”). 3. Click **Save** to submit the information. ![SMS test device configuration form showing fields for device name, phone number in E.164 format, and optional description.](https://docs.pushwoosh.com/test-your-integration-test-devices-4.webp) 4. A verification code will be sent to the phone number you entered. 5. Enter the code in the next step to confirm the device registration. Once the device is verified, it will appear in the **Test Devices** list, ready for testing SMS campaigns. ## Find a test device The **Test Devices** page lists the application’s test devices, newest first, 20 per page. Each row shows the **Device name** with its description, **HWID**, **Platform**, and **Date added**. Use **Show on page** to change the page size. To find a device, type in the **Search** field. Search matches the device name, description, platform, or any part of the HWID. ![Test Devices page with a partial HWID typed in the Search field and the matching test devices listed](https://docs.pushwoosh.com/test-your-integration-test-devices-7.webp) Click a device name to open the profile of the user that device belongs to in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). A name without a link means Pushwoosh has no subscriber with that HWID in this application. You can also browse test devices from User Explorer: with an empty search, open its [**Test devices** tab](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#test-devices-tab). --- # What is Pushwoosh? **URL:** https://docs.pushwoosh.com/product/first-steps/what-is-pushwoosh/ Reach customers on mobile and web with Pushwoosh, an omnichannel platform for push, email, in-app, SMS, WhatsApp, and more. --- [Youtube Video: Reach your customers on their favorite mobile & web channels with Pushwoosh, a comprehensive platform for omnichannel communication.](https://youtube.com/watch?v=Qe2mcMZ51wQ) Pushwoosh is an omnichannel customer engagement platform trusted by a diverse clientele of over 80,000 businesses spanning various industries. The platform is designed to help businesses generate revenue through effective communication. It provides several tools for engaging users, including: * [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview), which enables automated, trigger-based communication flows that guide users through personalized scenarios; * [Messaging channels](https://docs.pushwoosh.com/product/messaging-channels) that can be used for simple, one-time communications like push notifications, in-apps, emails, SMS, WhatsApp, Web popups, and more; * [API](https://docs.pushwoosh.com/developer/api-reference/), which allows developers to integrate and automate communications based on custom triggers and data. ## Who can benefit from Pushwoosh? Pushwoosh is an omnichannel customer engagement platform built to empower businesses across diverse industries, especially those owning mobile apps or planning to create one. However, even if a business does not have an app, it can still benefit from Pushwoosh by using web pushes, emails, WhatsApp, and advanced segmentation features. Here are some examples of how Pushwoosh can benefit businesses in different sectors: ### E-commerce Drive targeted promotions, boost conversions, and keep customers coming back for more with personalized push notifications, in-app messages, and emails. ### Gaming Re-engage players, announce updates and create exciting in-game events through tailored notifications and interactive messaging experiences. ### Mobility and delivery services Improve delivery efficiency, keep customers informed, and personalize the delivery journey with timely updates and targeted offers. ### Fintech and banking Deliver timely financial updates, and promote new services through reliable and personalized messaging. ### Logistics Track deliveries, inform customers about their shipment status, and improve logistics efficiency with real-time updates and targeted notifications. ### Lifestyle apps Motivate users, deliver personalized recommendations, and boost engagement with interactive messages and targeted campaigns that cater to their individual needs. ### Learning and education Send personalized notifications for course updates, content delivery, quiz reminders, and progress to enhance learner engagement and boost course completion rates. ### Dating Notify users of matches, chat messages, profile updates, event invitations, and other relevant interactions. ## Pushwoosh key features ### Multichannel messaging Reach your audience across multiple channels, all from one screen! Pushwoosh Customer Journey Builder allows you to send: * [Mobile push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications) * [Message inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox) * [In-App messages](https://docs.pushwoosh.com/product/messaging-channels/in-apps) * [Web push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications) * [Web popups](https://docs.pushwoosh.com/product/messaging-channels/web-popups) * [Emails](https://docs.pushwoosh.com/product/messaging-channels/emails) * [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms) * [WhatsApp messages](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages) * [LINE](https://docs.pushwoosh.com/product/messaging-channels/line/) * [Kakao](https://docs.pushwoosh.com/product/messaging-channels/kakao/) * [Telegram](https://docs.pushwoosh.com/product/messaging-channels/telegram/) * [Apple Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) * [Google Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) ### Omnichannel campaigns Effortlessly combine messaging channels to craft a multi-layered, personalized experience tailored to each recipient. ### Audience segmentation [Segment your audience](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation) based on their profiles, preferences, and behavior to send highly targeted messages. ### Personalization [Customize messages](https://docs.pushwoosh.com/product/personalization/dynamic-content) with user-specific details to increase relevance and engagement. ### Analytics and reporting Gain valuable insights into campaign performance with detailed [analytics and reporting](https://docs.pushwoosh.com/product/statistics-and-analytics/statistics-faq). ### Experimenting and testing Optimize your messaging strategy by conducting [A/B/n tests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split) to determine the most effective messages and delivery times. ### Automated communication Build automated, omnichannel [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) to engage users effectively and enhance their experience. ### Data security Prioritize data security to ensure the safety and privacy of user information. ### Powerful integrations Seamlessly [integrate with various platforms](https://docs.pushwoosh.com/product/integrations/) and services to gain deeper insights about your customers and drive better results. ### Flexible pricing Choose from flexible [pricing options](https://www.pushwoosh.com/pricing/) that suit your budget and goals. ### Lightning-fast customer support Our friendly and knowledgeable [customer support](https://help.pushwoosh.com/hc/en-us/requests/new) team is always available to help you whenever you need it. ## Pushwoosh use cases Pushwoosh is a versatile platform that caters to various industries, offering businesses innovative ways to connect with their audience, boost engagement, and drive success. Below, we’ll explore some industry-specific examples of how businesses can harness the power of Pushwoosh to achieve their objectives. ### E-commerce #### Abandoned cart recovery Use Pushwoosh to send automated reminders to users who abandoned their shopping carts, encouraging them to complete their purchases with personalized offers. #### Flash sales and discounts Promote flash sales, exclusive discounts, and limited-time offers, driving immediate sales and revenue growth. ### Gaming #### In-game events Leverage Pushwoosh to inform players about in-game events, special challenges, and rewards, enhancing player engagement and retention. #### Updates and patch notes Send push notifications with game updates, patch notes, and bug fixes to keep players informed and engaged, leading to a better gaming experience. ### Media **Personalized Content Recommendations** Use push notifications or in-app messages to suggest personalized content based on users’ viewing habits, preferences, or past interactions with the app. **Feedback Collection** Use in-apps to collect user feedback on the quality of content recommendations and overall satisfaction. ### Subscription-based apps #### Subscription renewal reminders Send push notifications to remind users of upcoming renewals or offer exclusive incentives to encourage them to extend their subscriptions. #### Personalized content recommendations Analyze user behavior to deliver tailored content suggestions, boosting user satisfaction and retention. ### Mobile and delivery **Real-time tracking** Utilize Pushwoosh to provide real-time order tracking and delivery updates, enhancing the customer experience. **Promotions and discounts** Send promotions to app users who order frequently or during peak hours. Retain these high-value customers and boost revenue. ### Fintech and banking #### Personalized financial insights Provide personalized financial insights and investment tips based on user profiles and transaction history, fostering customer loyalty. ### Travel #### Special offers and destination insights Use Pushwoosh to send personalized promotions and destination insights, enhancing the overall travel experience and driving bookings. ### Education #### Announcements and course updates Leverage Pushwoosh to deliver important announcements, course updates, and assignment reminders to students, ensuring they stay informed and engaged with their studies. #### Personalized learning recommendations Recommend personalized learning paths, course materials, and resources based on individual student progress, enhancing the overall learning experience and retention rates. These examples showcase just a glimpse of Pushwoosh’s potential across diverse industries. Explore our documentation and discover how you can tailor the platform to unlock success in your sector. --- # 3rd-party integrations **URL:** https://docs.pushwoosh.com/product/integrations/ Explore all available integrations in Pushwoosh. Connect CRM, analytics, payments, and marketing tools to sync data and automate campaigns. --- Pushwoosh offers seamless integration options with a variety of partner services to enhance your campaigns, including: * Customer data * Marketing and product analytics * In-app purchases and payment processing * Marketing automation To explore all available integrations in Pushwoosh, navigate to **Settings > 3rd-party integrations**. ![Integrations settings page showing 3rd-party integration options and setup guides](https://docs.pushwoosh.com/integrations-3rd-party-integrations-1.webp) To learn how to set up an integration, click on **Setup Guide**. To begin the setup process, click on **Login page**, which will redirect you to the third-party service login page. ## Pushwoosh integrations overview ### Customer data platform and data streaming Utilize these services to personalize and improve Pushwoosh campaigns with detailed customer data. [Event streaming ](https://docs.pushwoosh.com/product/integrations/event-streaming/) [Google BigQuery ](https://docs.pushwoosh.com/product/integrations/google-bigquery-integration/) [Inbound webhook ](https://docs.pushwoosh.com/product/integrations/inbound-webhook/) [Make ](https://www.make.com/en/integrations/pushwoosh) [mParticle ](https://docs.pushwoosh.com/product/integrations/mparticle-integration/) [RudderStack ](https://docs.pushwoosh.com/product/integrations/rudderstack-integration/) [Segment ](https://docs.pushwoosh.com/product/integrations/segment-integration/) [Solitics ](https://docs.pushwoosh.com/product/integrations/solitics-integration/) ### Marketing analytics/MMP Leverage these integrations to measure and refine the impact of your marketing efforts, ensuring every campaign launched in Pushwoosh is data-backed. [Adjust ](https://docs.pushwoosh.com/product/integrations/adjust-integration/) [AppsFlyer ](https://docs.pushwoosh.com/product/integrations/appsflyer-integration/) [AppsFlyer ESP 2.0 (email) ](https://docs.pushwoosh.com/product/integrations/appsflyer-esp2-email-integration/) [Meta Ads ](https://docs.pushwoosh.com/product/integrations/meta-ads-integration/) [Singular ](https://docs.pushwoosh.com/product/integrations/singular-integration/) ### Product analytics Incorporate user interaction data from these services to segment and personalize Pushwoosh campaigns. [Amplitude ](https://docs.pushwoosh.com/product/integrations/amplitude-integration/) [devtodev ](https://docs.devtodev.com/3rd-party-sources/cohort-export/pushwoosh) [Google Analytics ](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/) [Mixpanel ](https://docs.pushwoosh.com/product/integrations/mixpanel-integration/) [Piano ](https://docs.pushwoosh.com/product/integrations/piano-analytics-integration/) ### In-app purchases and payment processing Analyze in-app purchase and payment data from these services and use the insights in Pushwoosh to craft targeted messages that boost your app’s revenue. [Adapty ](https://docs.pushwoosh.com/product/integrations/adapty-integration/) [App Store subscription tracking ](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/) [Apphud ](https://docs.apphud.com/docs/pushwoosh) [Google Play subscription tracking ](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/) [Qonversion ](https://docs.pushwoosh.com/product/integrations/qonversion-integration/) [RevenueCat ](https://docs.pushwoosh.com/product/integrations/revenuecat-integration/) [Stripe ](https://docs.pushwoosh.com/product/integrations/stripe-integration/) ### E-commerce platforms These integrations sync store data such as customers, orders, and abandoned carts from your e-commerce platform to Pushwoosh for real-time messaging and segmentation. [Magento ](https://docs.pushwoosh.com/product/integrations/magento-integration/) [Shopify ](https://docs.pushwoosh.com/product/integrations/shopify/) [WooCommerce ](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/) ### Analytics Monitor crashes, diagnose issues, and target notifications only to users affected by specific problems. [Crashlytics ](https://docs.pushwoosh.com/product/integrations/crashlytics-integration/) ### CMS/Low-code platforms Leverage Pushwoosh functionality within OutSystems low-code development platform. [OutSystems ](https://www.outsystems.com/forge/list?q=pushwoosh&t=&o=most-popular&tr=False&oss=False&c=%20&a=&v=&hd=False&tn=&scat=forge) [WebViewGold ](https://docs.pushwoosh.com/product/integrations/webviewgold-integration/) ### Travel and logistics Watch a booking’s flight and react to gate, delay, and status changes in real time. [Flight Status ](https://docs.pushwoosh.com/product/integrations/flight-status-integration/) ### Design tools Send images from your design tool straight to the Media storage of a Pushwoosh application. [Canva ](https://docs.pushwoosh.com/product/integrations/canva-integration/) [Figma ](https://docs.pushwoosh.com/product/integrations/figma-integration/) ### Marketing automation & CRM Streamline your marketing with automation and customer relationship management by integrating Pushwoosh with leading platforms. [HubSpot ](https://docs.pushwoosh.com/product/integrations/hubspot-integration/) [Marketo ](https://docs.pushwoosh.com/product/integrations/marketo-integration/) --- # Adapty integration **URL:** https://docs.pushwoosh.com/product/integrations/adapty-integration/ Enhance user engagement by updating profile Tags in Pushwoosh with subscription events from Adapty. Follow the steps below for seamless integration. --- Developer assistance needed You will need help from your development team to set up the integration. Share this guide with them. ## Integration overview and use cases [Adapty](https://app.adapty.io/login) is a mobile app subscription management and monetization platform. By integrating Adapty with Pushwoosh, you can pass any subscription-related events and paywall interactions from Adapty to Pushwoosh. There, you can initiate targeted campaigns to engage your user base, promote upsells and stimulate renewals. Thanks to this integration, subscription businesses can make their communications more efficient and achieve a substantial revenue uplift. ## Setting up the integration 1. **Start in your** [**Adapty account**](https://app.adapty.io/login) Navigate to the Integrations section, select Pushwoosh, activate it by switching the toggle from off to on, and then complete the necessary fields. ![Pushwoosh and Adapty integration - step 1](https://docs.pushwoosh.com/integrations-adapty-integration-1.webp) 2. **Go to Pushwoosh to set up essential credentials** Establish a connection between your Pushwoosh and Adapty accounts by setting up credentials. This requires your Pushwoosh App ID and authentication token. ![Pushwoosh and Adapty integration - step 2](https://docs.pushwoosh.com/integrations-adapty-integration-2.webp) 3. **Locate your Pushwoosh credentials** * App ID: Found in the Pushwoosh dashboard. * Auth Token: Located in the API Access section of Pushwoosh Settings. ![Pushwoosh and Adapty integration - step 3](https://docs.pushwoosh.com/integrations-adapty-integration-3.webp) 4. **Configure Events and Tags** Below the credential settings, you’ll find options to select and rename various event groups from Adapty to send to Pushwoosh. Review and choose the events you need. For a comprehensive list of events available in Adapty, refer to [their detailed guide](https://docs.adapty.io/docs/events). Adapty handles the transmission of subscription events to Pushwoosh through a server-to-server integration, allowing full visibility of these events in your Pushwoosh Dashboard. ![Pushwoosh and Adapty integration - step 4](https://docs.pushwoosh.com/integrations-adapty-integration-4.webp) 5. **Enable custom tags** Enhance your Pushwoosh integration with Adapty by utilizing custom tags. These tags can be tailored to your specific requirements, as outlined below: * adapty_customer_user_id: A unique user identifier from Pushwoosh. * adapty_profile_id: Unique Adapty User Profile ID, visible in the Adapty dashboard. * environment: Identifies the user’s environment, either ‘Sandbox’ or ‘Production’. * store: Indicates the purchasing store (‘app_store’ or ‘play_store’). * vendor_product_id: The product ID in the Apple/Google store. * subscription_expires_at: The latest subscription’s expiration date, in a specific format. * last_event_type: Type of the most recent event from Adapty. * purchase_date: Date of the last transaction, in a specific format. * original_purchase_date: Date of the first purchase, in a specific format. * active_subscription: Indicates the subscription status. * period_type: Latest period type of the purchase or renewal. Additionally, you can integrate custom attributes for greater tracking flexibility. On the integration page on Adapty’s side, mark the ‘Send user custom attributes’ checkbox for automatic transmission to Pushwoosh. 6. **Required: Configure SDK** To complete the integration, send the HWID value from Pushwoosh to Adapty: * Swift ```plaintext let params = AdaptyProfileParameters.Builder() .with(pushwooshHWID: Pushwoosh.sharedInstance().getHWID()) .build() Adapty.updateProfile(params: params) { error in // handle the error } ``` * Kotlin ```plaintext val params = AdaptyProfileParameters.Builder() .withPushwooshHwid(Pushwoosh.getInstance().hwid) .build() Adapty.updateProfile(params) { error -> if (error != null) { // handle the error } } ``` * Java ```plaintext AdaptyProfileParameters params = new AdaptyProfileParameters.Builder() .withPushwooshHwid(Pushwoosh.getInstance().getHwid()) .build(); Adapty.updateProfile(params, error -> { if (error != null) { // handle the error } }) ``` * Flutter ```plaintext import 'package:pushwoosh/pushwoosh.dart'; final builder = AdaptyProfileParametersBuilder() ..setPushwooshHWID( await Pushwoosh.getInstance.getHWID, ); try { await adapty.updateProfile(builder.build()); } on AdaptyError catch (adaptyError) { // handle error } catch (e) {} ``` * TypeScript ```plaintext import { adapty } from 'react-native-adapty'; import Pushwoosh from 'pushwoosh-react-native-plugin'; // ... try { await adapty.updateProfile({ pushwooshHWID: hwid, }); } catch (error) { // handle `AdaptyError` } ``` * Unity ```plaintext var builder = new Adapty.ProfileParameters.Builder(); builder.SetPushwooshHWID(Pushwoosh.Instance.HWID); Adapty.UpdateProfile(builder.Build(), (error) => { // handle error }); ``` Note Read more about [possible use cases of the Pushwoosh and Adapty integration](https://blog.pushwoosh.com/blog/adapty-integration/) on our blog --- # Adjust integration **URL:** https://docs.pushwoosh.com/product/integrations/adjust-integration/ Use Adjust data to get comprehensive analytics and create effective marketing campaigns --- Developer assistance needed You will need help from your development team to set up the integration. Share this guide with them. [Adjust](https://suite.adjust.com/login) is a mobile measurement tool allowing you to analyze your marketing efficiency and make smarter marketing decisions based on the full picture of users behaviors. Being integrated with Pushwoosh, Adjust submits in-app events to your Pushwoosh app so that you can get the exhaustive analytics and build comprehensive user-centric marketing campaigns. To connect your [Adjust app](https://suite.adjust.com/login) to Pushwoosh, do the following: 1. [Integrate the Pushwoosh SDK](https://docs.pushwoosh.com/product/integrations/adjust-integration/#integrate-pushwoosh-sdk) into your app and [configure mobile platforms](https://docs.pushwoosh.com/product/integrations/adjust-integration/#configure-platforms) for the app in Pushwoosh Control Panel. If you have already done this, skip this step. 2. [Create Events](https://docs.pushwoosh.com/product/integrations/adjust-integration/#create-events) you are going to trigger from the Adjust app. 3. [Link the Adjust app with the Pushwoosh app](https://docs.pushwoosh.com/product/integrations/adjust-integration/#connect-adjust-to-pushwoosh). ## Integrate Pushwoosh SDK In case you have no Pushwoosh SDK integrated into your app, refer to our step-by-step integration guides: * [Setting up Pushwoosh **iOS** SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk) * [Integrate Pushwoosh **Android** SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk) * [**Windows** SDK integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/windows) ## Configure platforms If needed, configure app platforms in Pushwoosh Control Panel by following the corresponding guides: * [**iOS** Platform Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration) * [Configure **Android** platform](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * [**Windows** Configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/windows-configuration) ## Create Events To set up transmitting in-app events from Adjust to Pushwoosh, create Events in your Pushwoosh Control Panel. Please note that Events should be created **before connecting Pushwoosh to Adjust project**. Note Refer to the [Events guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) to learn how to create Events and attributes. Caution There’s a list of events with predefined names. Please make sure that you use these names while creating events in Pushwoosh exactly as follows: * reattribution * updatedAttribution * erasedUsers * uninstall * reinstall * reattributionReinstall ## Connect Adjust to Pushwoosh 1. In your Adjust dashboard, go to **Partner Setup** and press **Add partners**. ![Adjust Partner Setup page showing add partners option](https://docs.pushwoosh.com/integrations-adjust-integration-2.webp) 2. Search for Pushwoosh and press **+** button. ![Adjust partner search showing Pushwoosh integration option](https://docs.pushwoosh.com/integrations-adjust-integration-3.webp) 3. Activate the Pushwoosh module by entering your **Pushwoosh app code**. ![Adjust module activation form with Pushwoosh app code field](https://docs.pushwoosh.com/integrations-adjust-integration-4.webp) 4. To set up transmitting in-app events from Adjust to Pushwoosh, go to **Event Linking**. For each event created in your Adjust app, enter the corresponding Pushwoosh Event name. Then, press OK to save changes. ![Adjust Event Linking configuration page for Pushwoosh events](https://docs.pushwoosh.com/integrations-adjust-integration-5.webp) If you’re going to use the **same names** for Adjust and Pushwoosh events, press **Fill all with event names**. ![Adjust fill event names option for Pushwoosh integration](https://docs.pushwoosh.com/integrations-adjust-integration-6.webp) Note For more details, please refer to the [Adjust guide on integrating Pushwoosh](https://help.adjust.com/en/partner-setup/pushwoosh). ### Parameters transferred to Pushwoosh #### Automatically transmitted parameters Here’s a list of parameters which are automatically transmitted from Adjust to Pushwoosh when the module is activated: * Raw device IDs * App metadata * Tracker token * Full tracker names * Last tracker * Last tracker name * Partial tracker names * Impression Based flag * Organic flag * Click referrer * Activity Kind * Timestamps (rounded to the nearest hour) * Timestamps * Hashed device IDs * Attribution method * Adjust reference tag * Referrer values * User agents * IP addresses * Event tokens * Time spent * Session metadata * App lifetime session count * Reattribution flag * Deep link data * All partner parameters (if you activate them) * Revenue data (if you activate it) * Ad Spend (Cost) data * Google click IDs * OS name and versions #### Sending custom parameters You can send any custom parameters from Adjust to Pushwoosh. Any parameters that you send in the Adjust SDK will be forwarded to Pushwoosh. Please refer to the [Adjust documentation](https://help.adjust.com/en) to learn more. --- # Amplitude integration **URL:** https://docs.pushwoosh.com/product/integrations/amplitude-integration/ Supercharge your Pushwoosh Campaigns with Amplitude Integration --- Developer assistance needed You will need help from your development team to set up the integration. Share this guide with them. [Amplitude](https://amplitude.com/), a robust product analytics platform, empowers businesses to understand user behavior in real-time, track in-product interactions, and gain valuable insights into user engagement, retention, and revenue. Integrating Amplitude with Pushwoosh allows you to personalize Pushwoosh campaigns with Amplitude data, trigger automated campaigns based on user actions, analyze performance for continuous improvement, and ultimately drive enhanced engagement, reduced churn, and increased revenue. ## Integration overview ### Integration type **Destination:** Data is sent from Amplitude to Pushwoosh. ### Prerequisites To set up the Event Streaming and Cohort Sync integration from Amplitude to Pushwoosh, ensure the following prerequisites are met: * You need an active Pushwoosh account. * Generate an [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token) in Pushwoosh to send data. * Locate your Pushwoosh [Application Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) before configuring the integration. * This integration is only available to Amplitude customers with paid plans. * You must have a paid Pushwoosh plan to use this integration. ### Glossary (mapping entity names if they are different) **Cohorts** in Amplitude = **Segments** in Pushwoosh ### Synchronized entities * Events * Tags * Segments ### How does the integration work? The Amplitude Integration with Pushwoosh operates by syncing data between the two platforms to enhance user engagement and segmentation. Here is a detailed breakdown of how it works: ##### Data Transmission * **Events**: Amplitude sends event data to Pushwoosh. Each event contains a user ID (`external_id`), an application code (`app_id`), the event name (`event_name`), a timestamp (`created_at`), and event properties (`properties`). * Pushwoosh takes the platform from `properties.platform` (`iOS`, `Android`, or `Web`). An event without it is recorded as an iOS event. * Pushwoosh takes the device ID from `properties.device_id`. Without it, the user ID is used as the device ID. * Property values of any type are accepted and stored as event attributes. Numbers and booleans are kept as their text, and empty (`null`) values are skipped. * A new event is created in Pushwoosh automatically on first receipt. Its attribute types are inferred from the values: date, integer, boolean, price, or string. When an existing event gets a new property, Pushwoosh adds it as an attribute automatically, up to 100 attributes per event. Such attributes are typed from the text only, as date, version, or string, so a numeric property added later becomes a string attribute. * **Tags**: Pushwoosh receives user properties and saves them as tags on the user. Each request contains a user ID (`external_id`, required), an application code (`app_id`), and user properties (`properties`). * Missing tags are created automatically, with the type inferred from the value the same way as for event attributes. * Properties named like Pushwoosh default tags, such as Language, Country, or Timezone, are ignored. * **Segments**: For each synced cohort, Pushwoosh creates a segment with the cohort name. Cohort membership is stored as a value of the **CSV import** list tag on the user, and users are matched by User ID. Users are added to or removed from the segment as Amplitude syncs the cohort. ##### Data Flow * **Events and Tags**: Data is sent from Amplitude to Pushwoosh based on user interactions or attributes tracked in Amplitude. Events and tags are created or updated in Pushwoosh according to the configuration and mappings set up in Amplitude. * **Segments**: Segments are created in Pushwoosh when Amplitude makes a request. Pushwoosh generates a unique identifier for each segment, which Amplitude uses to manipulate the segment by adding or removing users. ##### Requests Requests are sent to specific endpoints in Pushwoosh to handle events, user attributes, and segment updates. Each request requires authentication and has specific headers and payload formats. ### Use cases ##### Event tracking Track user actions and interactions from Amplitude and send these events to Pushwoosh to trigger notifications or updates. ##### User tagging Assign tags to users based on their behavior or attributes in Amplitude. These tags can be used in Pushwoosh for targeted messaging or segmentation. ##### Segment management Create and manage user segments in Pushwoosh based on the data and segments configured in Amplitude. This helps in targeting specific groups of users with personalized messages. ##### Automated user management Automatically add or remove users from segments based on their actions or attributes in Amplitude, ensuring up-to-date segmentation in Pushwoosh. ## Setting up the integration ### Cohort sync ##### Pushwoosh setup 1. In Pushwoosh, create or copy an [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token) in **Settings > API Access**. 2. Copy the [Application Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) of the app you want to sync cohorts to. ##### Amplitude setup To configure Amplitude for integration, follow these steps: 1. In your Amplitude **Data**, go to the **Catalog** and select **Destinations**. 2. Under the Cohort section, locate and click **Pushwoosh**. ![Choosing Pushwoosh from destinations](https://docs.pushwoosh.com/integrations-amplitude-integration-2.webp) 3. Click **Add another destination**. ![Adding another destination](https://docs.pushwoosh.com/integrations-amplitude-integration-3.webp) 4. Provide a name and paste the API Access Token you copied from Pushwoosh. ![Adding Pushwoosh API access token and application code](https://docs.pushwoosh.com/integrations-amplitude-integration-4.webp) 5. Map the Amplitude User ID field to the Pushwoosh User ID field. Pushwoosh matches cohort members by User ID, so the Amplitude field you select must contain the same value that your app sets as the User ID in Pushwoosh. Amplitude’s setup page states that the field must contain an email address. If your app sets a different value as the Pushwoosh User ID, cohort members aren’t matched. In that case, set the user’s email as the User ID in the Pushwoosh SDK, or contact Pushwoosh support. 6. Save your settings when you are done. ##### Sending a cohort To synchronize your first cohort, complete the following steps: 1. In Amplitude, open the cohort you wish to synchronize, then click on **Sync**. ![Sending a cohort](https://docs.pushwoosh.com/integrations-amplitude-integration-5.webp) 2. Choose **Pushwoosh** and select the account you want to synchronize with. Then, click **Next**. ![Selecting Pushwoosh as sync destination](https://docs.pushwoosh.com/integrations-amplitude-integration-6.webp) 3. Decide on the synchronization cadence and save your configurations. ![Setting synchronization cadence](https://docs.pushwoosh.com/integrations-amplitude-integration-7.webp) ### Event streaming ##### Pushwoosh setup 1. Create or copy an [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token) in **Settings > API Access**. 2. Copy the [Application Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) of your app. ##### Amplitude setup 1. In Amplitude, go to **Data Destinations** and find **Pushwoosh - Event Stream**. ![Data Destinations showing Pushwoosh Event Stream option](https://docs.pushwoosh.com/integrations-amplitude-integration-8.webp) 2. Enter a sync name and then click **Create Sync**. ![Amplitude sync name creation form](https://docs.pushwoosh.com/integrations-amplitude-integration-9.webp) 3. Change the Status from **Disabled to Enabled** and paste your Pushwoosh API Access Token and Application Code. ![Enabling Pushwoosh Event Stream with API credentials](https://docs.pushwoosh.com/integrations-amplitude-integration-10.webp) 4. From the left dropdown, select the Amplitude user property that corresponds to your Pushwoosh UserID. 5. (Optional) Enable the **Create & Update users** toggle if you want to send users and their properties in real-time whenever Amplitude creates or updates a user property. ![Create and Update users toggle option](https://docs.pushwoosh.com/integrations-amplitude-integration-11.webp) 6. Enable **Send Events** (“Events are sent to Pushwoosh”) if you want to stream events to Pushwoosh. This will automatically forward events to Pushwoosh as they are ingested in Amplitude. Events are not sent on a schedule or on demand using this integration. ![Send Events option for real-time event streaming](https://docs.pushwoosh.com/integrations-amplitude-integration-12.webp) 8. Then, choose which events you want to send in the **Select and Filter Events** section. Only select the events you need in Pushwoosh; transformed events are not supported. 9. Once done, enable the destination and click **Save**. ## How do you know that the integration is working? To verify that the Amplitude and Pushwoosh integration is functioning correctly, you can use the following methods: ##### Monitor Pushwoosh dashboard * **Events and Tags**: Check the Pushwoosh dashboard to see if the events and tags sent from Amplitude appear as expected. Look for recent activities or updates related to your events and tags. * **Segments**: Verify that segments are being created, updated, or modified based on the data from Amplitude. Ensure that user counts and segment memberships reflect expected changes. ##### Check API responses * **Successful responses**: Ensure that you receive `200 OK` responses from the Pushwoosh API endpoints. Although there may not always be a response body, the `200 OK` status indicates that the request was successfully received. * **Error responses**: Review status codes other than `200 OK` for clues if something is not working: * `400 Bad Request`: the request body is not valid JSON, or the user ID is missing. * `401 Unauthorized`: the API token or the application code is missing, invalid, or the token belongs to another account. * `503 Service Unavailable`: Pushwoosh is temporarily unable to process the event. The request can be retried. * Other `5xx` codes: an internal error while checking the API token, saving tags, creating a segment, or updating cohort membership. Retry the request later. * Events that Pushwoosh rejects because of their content get `200 OK` and are not processed, so a `200 OK` alone doesn’t confirm that an event was recorded. ##### Log requests and responses * **Request logs**: Maintain logs of outgoing requests from Amplitude and incoming responses from Pushwoosh. Check these logs to ensure that requests are formatted correctly and responses are as expected. * **Error logging**: Implement error logging to capture any issues during data transfer or integration processes. ##### Test with sample data * **Send test Events/Tags**: Create and send test events and tags from Amplitude to Pushwoosh. Verify that these test entries appear correctly in Pushwoosh and trigger expected behaviors. * **Simulate user actions**: Perform actions in Amplitude that should trigger specific events or updates in Pushwoosh. Check if these actions result in the expected changes in Pushwoosh. ##### Use monitoring tools * **Integration monitoring**: Utilize monitoring tools or services that can track the health and performance of the integration. These tools can alert you to issues or failures in real-time. * **Analytics and reporting**: Use analytics features in both Amplitude and Pushwoosh to review data flow and integration performance. ##### Review integration documentation * **Configuration review**: Double-check the integration configuration in both Amplitude and Pushwoosh to ensure all settings and mappings are correctly applied. * **Documentation updates**: Stay updated with any changes or updates in the integration documentation that might affect functionality. ##### Contact support If you encounter persistent issues or inconsistencies, reach out to the support teams of Amplitude or Pushwoosh for assistance. Provide them with details of the integration setup and any error logs for faster resolution. ## Troubleshooting ##### Integration issues * **Event not appearing in Pushwoosh**: Ensure the event is correctly mapped, and verify that the Pushwoosh application code and API token are properly configured. * **Tags not syncing**: Confirm that the tags are formatted correctly and that all necessary authentication details are included in the request. ##### Segment problems * **Segment not created**: Check that the request to create the segment is correctly formatted and that the Pushwoosh API token is valid. * **Users not added/removed**: Verify that the user IDs are correctly specified and that the segment ID is valid. Ensure the request payload is properly formatted. ##### Response issues * **No response body**: A `200 OK` response has no body. It doesn’t confirm that an event was recorded, because events rejected for their content also get `200 OK` (see [Check API responses](https://docs.pushwoosh.com/product/integrations/amplitude-integration/#check-api-responses)). Check the event in Pushwoosh as described in [Monitor Pushwoosh dashboard](https://docs.pushwoosh.com/product/integrations/amplitude-integration/#monitor-pushwoosh-dashboard). ## FAQ ### What should I do if I receive a `400 Bad Request` error? Pushwoosh returns `400` when the request body is not valid JSON or the user ID (`external_id`) is missing. Check that the user ID mapping in Amplitude points to a property that is filled for every user. ### How can I verify if the integration is working? Check the Pushwoosh dashboard for incoming events, tags, and segment updates. You can also monitor logs or responses from API endpoints for successful data transfers. ### Can I customize the events or tags sent to Pushwoosh? Yes, you can customize events and tags within the Amplitude configuration. Make sure that the data adheres to the expected Pushwoosh format. ### How often is the data synced between Amplitude and Pushwoosh? Data syncs in real-time or near-real-time based on requests made from Amplitude. Ensure your configuration supports the desired update frequency. ### Where can I find more information or support for this integration? Refer to the official documentation from Amplitude and Pushwoosh. For further assistance, contact the respective support teams. --- # App Store subscription tracking **URL:** https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/ Connect App Store Server Notifications to Pushwoosh and track purchases, renewals, cancellations, billing issues, and refunds. --- Developer assistance needed You’ll need help from your development team to set up this integration. Please share this guide with them. ## Integration overview [App Store Server Notifications](https://developer.apple.com/documentation/appstoreservernotifications) is Apple’s server-to-server service that sends your backend a real-time message whenever a subscription’s status changes. By connecting App Store Server Notifications to Pushwoosh, you can react to the entire subscription lifecycle, including purchases, renewals, cancellations, billing problems, expirations, and refunds — without building your own backend infrastructure. Whenever a subscription’s status changes in a user’s App Store account, Apple notifies Pushwoosh, and Pushwoosh fires the matching [`PW_Subscription*`](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/#tracked-events) event on the user profile. Note This integration supports **iOS subscriptions** (App Store Server Notifications V2). To track Android subscriptions, see [Google Play subscription tracking](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/). ### Integration type **Source:** App Store Server Notifications are sent from Apple to Pushwoosh. ### Tracked events Pushwoosh maps every supported App Store notification to a unified `PW_Subscription*` event set, so you can trigger campaigns on any stage of the subscription lifecycle. | Event | Fires when | | ----------------------------- | --------------------------------------------------------------------------------- | | `PW_SubscriptionStart` | A user buys the subscription for the first time. | | `PW_SubscriptionRenew` | The subscription auto-renews for a new billing period. | | `PW_SubscriptionCancel` | A user turns off auto-renewal. The subscription stays active until it expires. | | `PW_SubscriptionResume` | A user re-enables auto-renewal, or resubscribes before the subscription lapses. | | `PW_SubscriptionBillingIssue` | A renewal payment fails and the subscription enters Apple’s billing retry period. | | `PW_SubscriptionRecovered` | A previously failed renewal goes through and the subscription is active again. | | `PW_SubscriptionExpired` | The subscription has fully lapsed and is no longer active. | | `PW_SubscriptionRefund` | Apple refunds the purchase or revokes access. | Every event carries the same attributes: * **productID:** the App Store product identifier of the subscription. * **expiresAt:** when the current paid period ends, as a Unix timestamp in seconds. Included when Apple provides it. How events map to App Store Server Notifications For developers verifying the integration, each Pushwoosh event corresponds to these App Store `notificationType` (and `subtype`) values: | Pushwoosh event | `notificationType` / `subtype` | | ----------------------------- | -------------------------------------------------------------------------------- | | `PW_SubscriptionStart` | `SUBSCRIBED` / `INITIAL_BUY` | | `PW_SubscriptionRenew` | `DID_RENEW` | | `PW_SubscriptionCancel` | `DID_CHANGE_RENEWAL_STATUS` / `AUTO_RENEW_DISABLED` | | `PW_SubscriptionResume` | `DID_CHANGE_RENEWAL_STATUS` / `AUTO_RENEW_ENABLED`, `SUBSCRIBED` / `RESUBSCRIBE` | | `PW_SubscriptionBillingIssue` | `DID_FAIL_TO_RENEW` | | `PW_SubscriptionRecovered` | `DID_RENEW` / `BILLING_RECOVERY` | | `PW_SubscriptionExpired` | `EXPIRED` | | `PW_SubscriptionRefund` | `REFUND`, `REVOKE` | Other notification types, such as price increases, plan changes, on-hold, and consumption requests, are acknowledged but don’t post an event. ### How it works 1. A subscription’s status changes in a user’s App Store account (a purchase, renewal, cancellation, and so on). 2. Apple sends an App Store Server Notification (V2) to your Pushwoosh notification URL. 3. Pushwoosh decodes the signed payload and reads the `appAccountToken` from the transaction. 4. Pushwoosh looks up the device whose HWID matches that token, finds the user bound to it, and posts the matching `PW_Subscription*` event for that user. Important The match between an App Store transaction and a Pushwoosh user relies on the `appAccountToken`. If your app doesn’t set this token at purchase time, Pushwoosh receives the notification but **no event is posted**. See [how to set `appAccountToken`](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/#set-appaccounttoken-to-the-devices-pushwoosh-hwid). ### Use cases **Win back churning subscribers:** Disabling auto-renewal doesn’t end access right away. The subscription stays active until the paid period ends, and that’s your window to win the user back. On `PW_SubscriptionCancel`, launch a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) with a retention push, an [email](https://docs.pushwoosh.com/product/messaging-channels/emails/) about features they’d lose, or an [in-app message](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) with a renewal discount before access lapses. **Onboard new subscribers:** Trigger a welcome series on `PW_SubscriptionStart` to help users get value from their plan early and set the stage for renewal. **Rescue failed payments:** When `PW_SubscriptionBillingIssue` fires, a renewal payment didn’t go through and the subscription is in Apple’s retry window. Prompt the user to update their payment method before they lose access, and follow up with `PW_SubscriptionRecovered` to confirm once it’s resolved. **Re-engage lapsed users:** Start a reactivation campaign on `PW_SubscriptionExpired` with a returning-customer offer for subscribers who have fully churned. ## Setting up the integration ### Set `appAccountToken` to the device’s Pushwoosh HWID Pushwoosh identifies the right user from the device’s **HWID**, so your app must attach the device’s Pushwoosh HWID as the `appAccountToken` when the subscription is purchased through StoreKit. By default, the Pushwoosh iOS SDK uses the device’s `identifierForVendor` (IDFV) as the HWID. IDFV is already a `UUID`, exactly the format Apple requires for `appAccountToken`. Pushwoosh then resolves the user currently bound to that device automatically, so this works whether or not you assign your own User IDs with `setUserId`. * StoreKit 2 ```swift // Attach the device's Pushwoosh HWID (the default IDFV) as the appAccountToken var options: Set = [] if let hwid = UIDevice.current.identifierForVendor { options.insert(.appAccountToken(hwid)) } let result = try await product.purchase(options: options) ``` * StoreKit 1 ```swift // applicationUsername must be a UUID string to populate appAccountToken let payment = SKMutablePayment(product: product) payment.applicationUsername = UIDevice.current.identifierForVendor?.uuidString SKPaymentQueue.default().add(payment) ``` Caution If your app overrides the Pushwoosh HWID (for example, via the [`PushwooshKeychain`](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-keychain/) persistent HWID module or a custom value), set `appAccountToken` to that same HWID. The token must match the device’s HWID in Pushwoosh, or the event cannot be attributed. ### Find your Pushwoosh Application Code Open your application in the Pushwoosh Control Panel. Your **Application Code** (format `XXXXX-XXXXX`) is shown below the project name in the sidebar. You’ll need the Application Code to build the notification URL. ### Add the notification URL in App Store Connect 1. In [App Store Connect](https://appstoreconnect.apple.com/), go to **Apps → your app → App Information** (under *General*), and scroll to **App Store Server Notifications**. 2. Select **Version 2** notifications. 3. Set both the **Production Server URL** and the **Sandbox Server URL** to: ```plaintext https://appstore-notifications.pushwoosh.com/appstore/YOUR_APPLICATION_CODE/ ``` 4. Replace `YOUR_APPLICATION_CODE` with the Application Code from the previous step. Keep the trailing slash. ### Confirm the events in Pushwoosh Pushwoosh registers each `PW_Subscription*` event in your project the first time it occurs, with the `productID` and `expiresAt` attributes. After a sandbox test, open **Audience → Events** to verify the events appear. They are then ready for segmentation, statistics, and Customer Journeys. ### Build your campaign Create a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) with a [trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on any `PW_Subscription*` event, for example `PW_SubscriptionCancel` for win-back or `PW_SubscriptionStart` for onboarding, and add the messages you want to send. ## Testing App Store Server Notifications can be triggered in Apple’s **Sandbox** environment. To verify the integration: 1. Make a sandbox subscription purchase with `appAccountToken` set as described above. This fires `PW_SubscriptionStart`. 2. Disable auto-renewal from the device’s subscription management screen. This fires `PW_SubscriptionCancel`. 3. In Pushwoosh Control Panel, open the user profile and go to [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab). 4. Confirm that the events appear within a few moments. --- # AppsFlyer ESP 2.0 integration **URL:** https://docs.pushwoosh.com/product/integrations/appsflyer-esp2-email-integration/ Set up Pushwoosh with AppsFlyer ESP 2.0 to track email clicks, wrap links, and keep Universal Links and App Links working. --- Developer assistance needed You’ll need help from your development team to set up the integration. Share this guide with them. [AppsFlyer](https://www.appsflyer.com/) is a mobile attribution and marketing analytics platform. [AppsFlyer ESP 2.0](https://support.appsflyer.com/hc/en-us/articles/360004556077-Email-service-provider-ESP-integration-guide) is AppsFlyer’s integration for email service providers (ESPs). Connect Pushwoosh with ESP 2.0 to enable link tracking in email campaigns. In this integration, Pushwoosh wraps URLs, and the ESP 2.0 solution keeps deep linking working for iOS Universal Links and Android App Links when links are wrapped for tracking. AppsFlyer records each click on your Click Tracking Domain (CTD) before redirecting the user, giving you full visibility into email performance alongside your other marketing channels and letting you measure the real impact of email on user acquisition and engagement. ## Integration overview ### Prerequisites * Pushwoosh application with [email sending configured](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) * AppsFlyer account with the [AppsFlyer ESP 2.0 integration](https://support.appsflyer.com/hc/en-us/articles/360004556077-Email-service-provider-ESP-integration-guide). This is a premium feature. Contact your AppsFlyer CSM or AppsFlyer support to get access. ### Synchronized entities Email link clicks via your Click Tracking Domain are recorded as click statistics in Pushwoosh and as click events with attribution data in AppsFlyer. ### Use cases * **Email click tracking:** Measure email link clicks via AppsFlyer. * **Deep linking from email:** Keep Universal Links (iOS) and App Links (Android) working when links in emails are wrapped. ### How the integration works Once the integration is live, each click goes through three steps: 1. Pushwoosh replaces links in your emails with tracking URLs on your CTD, for example: `https://clickpush.yourdomain.com/json/1.3/emailRedirect?d=...` 2. When a user taps the link, the request goes to AppsFlyer via the configured CNAME. AppsFlyer records the attribution, handles deep linking, and redirects the user to the final URL or into the app via Universal Link / App Link. It also sends a GET request to Pushwoosh, passing your CTD in the Host header, so Pushwoosh can record the click: ```plaintext GET https:///json/1.3/emailRedirect Host: ``` 3. Pushwoosh receives the request and records the click for its own statistics. ## Setting up the integration Pushwoosh vs AppsFlyer Pushwoosh is only involved in the initial CTD setup. The rest of the integration is completed on the AppsFlyer side. ### Step 1. Create and activate a click tracking domain The **Click Tracking Domain (CTD)** is the domain part of a tracked link URL shown in an email message. It combines a subdomain with your main domain, for example, `clickpush.esp-integrations1.com`. The subdomain is created in your domain’s DNS settings with your domain provider, usually by adding a CNAME record. New CTD required Creating a new CTD is mandatory for the AppsFlyer ESP 2.0 integration, even if you are already using an existing click tracking domain. Using a new one avoids impacting traffic for your current email campaigns. For details, see AppsFlyer’s guide: [Set up your ESP integration with AppsFlyer](https://support.appsflyer.com/hc/en-us/articles/26967438815377-Set-up-your-ESP-integration-with-AppsFlyer#step-1-create-a-onelink-template-for-your-email-campaign). 1. Decide which subdomain you will use as your CTD (for example, `clickpush.esp-integrations1.com`). 2. In Pushwoosh, open **Settings**, then select **Configure Platforms**. 3. On the **Email channel configuration** page, scroll to **Custom tracking domain** and select **Enable**. Note The **Custom tracking domain** section is shown only after the email channel is configured for your application. If you do not see it, complete [email configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) first. ![Pushwoosh Control Panel: Settings, Configure Platforms, Email channel configuration, Custom tracking domain with Enable.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-2.webp) 4. Enter your CTD in the **Domain** field and select **Next**. ![Enable custom tracking domain dialog: Domain field, Cancel, and Next.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-3.webp) 5. Outside Pushwoosh, open the service where you manage public DNS for the domain of your CTD (typically your domain registrar, a DNS host, or your hosting account). In its DNS or zone editor for that domain, add the **CNAME** Pushwoosh shows on this step so your CTD points to `api.pushwoosh.com`. DNS propagation DNS changes usually take effect within minutes but can take up to 48 hours. 6. In Pushwoosh, select **Verify**. ![Custom tracking domain DNS step: CNAME to api.pushwoosh.com, Back, Verify, and Save.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-4.webp) 7. When verification succeeds, select **Save**. 8. If you need to change the **Domain** or DNS record, select **Back** and adjust the earlier steps. 9. After you click **Save**, your CTD appears in the **Custom tracking domain** list in **Email channel configuration**. ![Email channel configuration: Custom tracking domain section showing the saved CTD in the list.](https://docs.pushwoosh.com/integrations-appsflyer-esp2-email-integration-5.webp) To disable it later, select `...` next to your CTD, then select **Disable custom tracking domain**. Tip Before continuing, send a test email and confirm that the link uses your CTD. **Example tracked link** `https://clickpush.example.com/json/1.3/emailRedirect?d=...` ### Step 2. Start the integration in AppsFlyer In the [AppsFlyer platform](https://hq1.appsflyer.com/), start the ESP 2.0 integration. Follow the [AppsFlyer guide to set up your ESP integration](https://support.appsflyer.com/hc/en-us/articles/26967438815377-Set-up-your-ESP-integration-with-AppsFlyer). AppsFlyer will ask for the following details: * **Click-tracking domain:** your CTD (for example, `clickpush.example.com`) * **ESP endpoint:** `api.pushwoosh.com` (the CNAME target shown in Pushwoosh when you enable the custom tracking domain) AppsFlyer will validate the configuration. Keep the CTD CNAME pointing to **`api.pushwoosh.com`** until the validation is complete. ### Step 3. Update CNAME to AppsFlyer After AppsFlyer validates the setup, it will give you a routing domain (for example, `jyvls.espclicks.appsflyer.com`). Update your CNAME so the CTD points to AppsFlyer instead of Pushwoosh: **Before:** `clickpush.example.com` → `api.pushwoosh.com` **After:** `clickpush.example.com` → `jyvls.espclicks.appsflyer.com` (use the domain provided by AppsFlyer) Propagation time After you update the CNAME, the integration can take up to 24 hours to start working. After switching the CNAME, send a test email and verify that links open correctly and clicks appear in AppsFlyer. ## What happens next Once the change takes effect, click traffic goes through AppsFlyer’s infrastructure. AppsFlyer provides the routing layer, SSL, AASA (Apple App Site Association), and Android App Links. Pushwoosh continues to wrap links and record click statistics. No further configuration is needed in Pushwoosh. To learn more about setup and testing on the AppsFlyer side, see: [Set up your ESP integration with AppsFlyer ](https://support.appsflyer.com/hc/en-us/articles/26967438815377-Set-up-your-ESP-integration-with-AppsFlyer#step-5-test-the-link) [Email service provider integration guide ](https://support.appsflyer.com/hc/en-us/articles/360004556077-Email-service-provider-ESP-integration-guide) --- # AppsFlyer integration **URL:** https://docs.pushwoosh.com/product/integrations/appsflyer-integration/ Learn how to set up the AppsFlyer integration with Pushwoosh to track user behavior and boost mobile campaign engagement ROI. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. ## Integration overview [AppsFlyer](https://www.appsflyer.com/) is a mobile attribution and marketing analytics platform that helps you analyze user acquisition and engagement. It provides comprehensive data on user behavior within the app and allows you to track user acquisition sources. Using the information obtained from AppsFlyer, you can create effective marketing campaigns in Pushwoosh. Once the integration is configured, AppsFlyer will send the following information to Pushwoosh: * User’s in-app behavior as Events and their attributes; * User acquisition sources as the *Acquisition Source* Tag values; * AppsFlyer campaign IDs as the *Acquisition Campaign* Tag values. ### Use cases Using this data, you can build user segments and make precisely targeted and personalized campaigns in Pushwoosh. Below are two examples of leveraging AppsFlyer data to create effective customer journeys in Pushwoosh. **1. Create highly targeted campaigns in Pushwoosh using events tracked by AppsFlyer.** Let’s say you have a news app. You configured AppsFlyer to track the *SubscribedToNews* event, which fires every time a user subscribes to a news channel. This event has the category attribute indicating the type of news the user subscribes to: *politics*, *business*, or *sports*. Pushwoosh receives the information about this event with its attribute values. You can create three user segments based on three possible values: ![User segments based on news category attributes](https://docs.pushwoosh.com/integrations-appsflyer-integration-1.webp) Now you can create campaigns targeted at users who are interested in a particular type of news: ![Targeted campaigns for users interested in specific news categories](https://docs.pushwoosh.com/integrations-appsflyer-integration-2.webp) **2. Personalize messaging based on user behavior data from AppsFlyer.** In this example, we will use an online shopping app. Let’s say AppsFlyer tracks the *AddToWishlist* event that fires whenever a user adds an item to their wishlist. This event has the item attribute indicating the name of the item added to the wishlist. AppsFlyer sends the *AddToWishlist* event data to Pushwoosh, where you can personalize messages depending on users’ choices. For example, you can create a push preset offering a discount on an item a user has added to their wishlist. Instead of the item name, add a [Dynamic Content placeholder](https://docs.pushwoosh.com/developer/guides/personalization/dynamic-content/#personalizing-messages-sent-via-api-with-dynamic-content) with the *item* attribute: ![Dynamic content placeholder in Pushwoosh push preset](https://docs.pushwoosh.com/integrations-appsflyer-integration-3.webp) If a user adds an item to their wishlist, they will receive a push containing information about it. For example, those who select iPhone 14 Pro Max will see this message: ![Push notification preview for iPhone 14 Pro Max item offer](https://docs.pushwoosh.com/integrations-appsflyer-integration-4.webp) ## Setting up the integration Note The integration process is identical for both Android and iOS platforms. 1. Integrate the Pushwoosh SDK into your app if you haven’t already done so. For more detailed instructions, [read the guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview). Caution For correct operation, Pushwoosh iOS SDK version 6.5.7 or later and Pushwoosh Android SDK version 6.6.16 or later are required. 2. To ensure that the correct data is sent to AppsFlyer, integrate this code snippet into your code. Make sure that it is executed after both Pushwoosh SDK and AppsFlyer SDK are initialized. * Java ```plaintext String pwhwid = Pushwoosh.getInstance().getHwid(); String pwuserid = Pushwoosh.getInstance().getUserId(); Map customData = new HashMap(); customData.put("pushwoosh_hwid", pwhwid); customData.put("pushwoosh_user_id", pwuserid); AppsFlyerLib.getInstance().setAdditionalData(customData); ``` * Objective-C ```plaintext NSString *pwhwid = [[Pushwoosh sharedInstance] getHWID]; NSString *pwuserid = [[Pushwoosh sharedInstance] getUserId]; NSDictionary *customData = [[NSDictionary alloc] initWithObjectsAndKeys:pwuserid, @"pushwoosh_user_id", pwhwid, @"pushwoosh_hwid", nil]; [[AppsFlyerLib shared] setAdditionalData:customData]; ``` Danger When using custom Pushwoosh UserID values to identify and target users, make sure to run the code after calling the **setUserId()** method in the Pushwoosh SDK with the custom value. Track when the Pushwoosh UserID changes according to the logic in your mobile application and send the updated values to AppsFlyer promptly. This is important for the correct mapping of data sent with users in your Pushwoosh database. 3. Configure Pushwoosh integration in AppsFlyer. For this, in your [AppsFlyer account](https://hq1.appsflyer.com/auth/login), go to **Collaborate** > **Partner Marketplace**. In the Partners list, locate and choose Pushwoosh. ![Locating Pushwoosh in AppsFlyer](https://docs.pushwoosh.com/integrations-appsflyer-integration-5.webp) 4. Click **Set up integration**. Then you will be directed to the integration setup page. There, enable the **Activate partner** toggle. ![Enabling Activate partner toggle in AppsFlyer](https://docs.pushwoosh.com/integrations-appsflyer-integration-6.webp) Note For iOS apps, the Advanced Privacy Mode is enabled by default which allows controlling the availability of user-level attribution data via the AppsFlyer platform. Though AppsFlyer doesn’t send this type of data to Pushwoosh, keeping the Advanced Privacy toggle activated may interfere with data transmission. To ensure that Pushwoosh receives all the data, we recommend disabling the Advanced Privacy Mode in **App Settings**. Doing so will not compromise the privacy of user data. Pushwoosh is dedicated to managing data securely and responsibly, employing industry-standard security protocols to protect confidentiality. ![Disabling the Advanced Privacy Mode in AppsFlyer](https://docs.pushwoosh.com/integrations-appsflyer-integration-7.webp) 5. In the **General settings** section, enter your Pushwoosh credentials: * **API Access Token**. To get the token, in your Pushwoosh account go to **Settings > API Access** and copy the code. You can learn more about the API Access Token [in this guide](https://docs.pushwoosh.com/developer/api-reference/api-access-token). * **Application Code** (XXXXX-XXXXX) that you can find below your application name in your Pushwoosh account. ![Adding Pushwoosh credentials in AppsFlyer general settings](https://docs.pushwoosh.com/integrations-appsflyer-integration-8.webp) 6. In the **Default postback** section, select **All media sources, including organic**. ![Configuring the Default postback section](https://docs.pushwoosh.com/integrations-appsflyer-integration-9.webp) 7. In the **In-app event settings** section, enter your Pushwoosh **Application Code** and **API Access Token**. ![In-app event settings section with Application Code and API token](https://docs.pushwoosh.com/integrations-appsflyer-integration-10.webp) 8. Enable the **In-app event postback** toggle and configure the events and data to be included in the postbacks sent to Pushwoosh. ![In-app event postback toggle configuration in AppsFlyer](https://docs.pushwoosh.com/integrations-appsflyer-integration-11.webp) Choose one or more AppsFlyer events to send to Pushwoosh from the drop-down menu, and complete the necessary parameters. | Parameter | Description | | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **the mapped to partner event** | Even though this field is mandatory, don’t worry about what you type because it won’t change how events are named. Pushwoosh will still get the events with the same names as they appear in AppsFlyer. | | **for users from** | In this field, select whether postbacks are sent exclusively for users attributed to this partner or for all media sources, including organic. | | **including** | Choose how event data is shared with Pushwoosh:- **No values & no revenue:** Only send the event itself without any parameters (values or revenue). - **Values & no revenue:** Send all parameters except the revenue value. - **Values & revenue:** Send all event parameters, including the revenue value (if it exists in the event). To receive events with additional attributes sent to Pushwoosh, select Values and Revenue. | 9. Once the integration is configured, click **Save**. Note Before launching your campaigns, make sure to test the integration. Errors often occur during setup, so a quick test helps prevent wasting resources on campaigns with missing or incorrect data. ## Email click tracking with AppsFlyer ESP 2.0 To use email click tracking and preserve Universal Links / App Links when sending emails through Pushwoosh, you can set up the [AppsFlyer ESP 2.0 email integration](https://docs.pushwoosh.com/product/integrations/appsflyer-esp2-email-integration). --- # Canva integration **URL:** https://docs.pushwoosh.com/product/integrations/canva-integration/ Import Canva designs into the Pushwoosh Media storage, edit stored images in Canva, and save the edits back as new versions. --- The Canva integration links your [Canva](https://www.canva.com/) account to the [Media storage](https://docs.pushwoosh.com/product/content/media-store/) of your Pushwoosh applications. Import pages of your Canva designs as images, open a stored image in the Canva editor, or start a new design sized for a push, in-app, or email image. When you finish in Canva, the result is saved back to the Media storage. ## Integration overview ### Integration type **Source:** Images come from your Canva designs and are saved to the Pushwoosh Media storage. ### Prerequisites * The **Modify applications** permission for the application whose Media storage you import into. See [Multi-login accounts](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/). * A Canva account. ### How does the integration work? Each Pushwoosh user connects their own Canva account. The connection works for every application you can modify, and it sees only the designs in your Canva account. Teammates who want to import from Canva connect their own accounts. Pushwoosh exports the pages you select from Canva and stores them in the Media storage like any other image. An image edited in Canva is saved as a new version of the original, so you don’t end up with duplicates. ## Connect Canva Connect Canva on the **3rd-party integrations** page or right from the Media storage. ### Connect from 3rd-party integrations 1. Go to **Settings > 3rd-party integrations**. 2. On the **Canva** card in **Available services**, click **Connect**. ![Canva card in the Available services section of 3rd-party integrations with the Connect link](https://docs.pushwoosh.com/integrations-canva-integration-1.webp) 3. In the pop-up window, sign in to Canva and allow access for Pushwoosh. ### Connect from the Media storage 1. Go to **Content > Media storage**. 2. Click **Import from Canva**. 3. Click **Connect Canva**. 4. In the pop-up window, sign in to Canva and allow access for Pushwoosh. ### If the Canva window doesn’t open If your browser blocks the Canva window, allow pop-ups for the Control Panel and try again. ## Import designs into the Media storage 1. Go to **Content > Media storage**. 2. Click **Import from Canva** next to the upload area. 3. Find the design: type in **Search designs**, or click **Load more** below the list to see more designs. 4. Click the design. 5. If the design has several pages, select the pages to import. You can import up to 10 pages at a time. The first page is selected by default. 6. In **Format**, select **PNG** or **JPG**. 7. Click **Import**. When the import finishes, the images appear in the Media storage with the **Canva** badge. Each image is named after the design. When you import several pages, the page number is added to the name, for example `Summer sale, page 2`. While the import runs: * Stay on the Media storage page. If you leave it, the import stops. * You can close the **Import from Canva** panel. If the import stopped, the pages saved before that stay in the Media storage. Import the remaining pages again. ## Edit an image in Canva You can open any image from the Media storage in the Canva editor: images imported from Canva, uploaded from your device, or added from other sources. 1. Go to **Content > Media storage**. 2. On the image, click the **Edit in Canva** icon. The Canva editor opens in a new tab. 3. Edit the design in Canva. 4. Within 24 hours, return to Pushwoosh from Canva. See [Return to Pushwoosh in time](https://docs.pushwoosh.com/product/integrations/canva-integration/#return-to-pushwoosh-in-time). Wait for **Saved from Canva**. The edited image is stored as a new version of the original and keeps the original’s format. What you see in Canva: * **An image you imported from Canva:** the original Canva design opens, on the page you imported. * **Any other image, or a Canva design you no longer have access to:** Pushwoosh copies the image into a new design in your Canva account, and the Control Panel shows a message that you’re editing a copy. ### Versions after editing Each edit in Canva saves a new version of the image: * The Media storage shows the latest version. A badge such as **2 ver.** shows how many versions the image has. * Each version has its own URL. * Content that uses the previous URL keeps showing the previous version. To change the image there, copy the new URL and update the content. See [Where an image came from](https://docs.pushwoosh.com/product/content/media-store/#where-an-image-came-from). ### Return to Pushwoosh in time When you finish in Canva, click the **Return to** button in the top bar of the Canva editor, next to **Share**. Only then does Pushwoosh save your design to the Media storage. This works the same way when you edit an image and when you [create a new design](https://docs.pushwoosh.com/product/integrations/canva-integration/#create-a-new-design-in-canva). The design is saved if: * You return within 24 hours. After that, the return link expires and your changes stay only in Canva. To save them, open the image in Canva again. * You are signed in to Pushwoosh as the same user who opened Canva. After you return, the Media storage page opens, even if you opened Canva from the image picker in an editor. ## Create a new design in Canva To start a design from scratch at a size that fits your content: 1. Go to **Content > Media storage**. 2. Click **Create in Canva**. 3. Select a size: * **Push big picture · 1024×512** * **In-app / Rich Media · 1080×1920** * **Email header · 600×300** 4. Design the image in the Canva tab that opens. 5. Within 24 hours, return to Pushwoosh from Canva. See [Return to Pushwoosh in time](https://docs.pushwoosh.com/product/integrations/canva-integration/#return-to-pushwoosh-in-time). The design is saved to the Media storage as a new PNG image with the **Canva** badge. The image is named after the size, for example `Pushwoosh 1024×512`. **Create in Canva** and **Edit in Canva** appear only after you connect Canva. ## Image limits and errors ### Image limits Every image saved from Canva follows the Media storage limits, whether you import it, edit it, or create it in Canva. | Limit | What happens when an image exceeds it | Message in the Control Panel | | ------------- | --------------------------------------------------------------------------------- | ---------------------------------------------- | | 10 MB | A PNG is saved as JPG, and its transparent areas become white. A JPG isn’t saved. | **file too large (max 10 MB)** for a JPG | | 25 megapixels | The image isn’t saved. | **image resolution exceeds limit (max 25 MP)** | To save an image that exceeds a limit, reduce its size in Canva and import it again. ### If an import fails If one page of an import fails, the import stops at that page: * Pages saved before it stay in the Media storage. * Pages after it aren’t imported. ### Other messages These messages can appear when you connect Canva or import and edit designs. | Message | Cause | What to do | | ---------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------- | | **Could not connect Canva** | You declined access in the Canva window, or the connection didn’t complete. If you close the Canva window without answering, nothing changes. | Connect Canva again. | | **the Canva connection is no longer valid, connect Canva again** | Canva no longer accepts your connection. | Connect Canva again. | | **the design is not found or no longer shared with you** | The design was deleted, or its owner stopped sharing it with you. | Choose another design. | | **Canva rate limit, retry in…** | Canva is temporarily limiting requests. | Wait for the time shown and try again. | To connect Canva again, click **Connect** on the **Canva** card, or **Connect Canva** in the **Import from Canva** panel. ## Disconnect Canva 1. Go to **Settings > 3rd-party integrations**. 2. On the **Canva** card in **Connected services**, click **Disconnect**. You can also click **Disconnect** next to your Canva account name in the **Import from Canva** panel of the Media storage. Disconnecting removes only your own connection. Images you already imported stay in the Media storage, and other users’ connections keep working. If the **Canva** card shows **Reconnect required**, Canva no longer accepts your connection. Click **Connect** and allow access again. ## FAQ ### Can I import from Canva with an API token? No. Canva is connected per Pushwoosh user, so importing and editing work only when you’re signed in to the Control Panel. ### Which formats can I import? PNG or JPG, up to 10 MB and 25 megapixels per image. An image edited in Canva keeps the format of the original: a JPG stays JPG, any other format is saved as PNG. ### Can my teammates use my Canva connection? No. Each teammate connects their own Canva account and sees only their own designs. --- # Crashlytics integration **URL:** https://docs.pushwoosh.com/product/integrations/crashlytics-integration/ Target users who experience particular crashes in the app without having to bother the rest of your audience --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. Trust is something you can build over time and lose in a blink of an eye. Once you’ve brought people into your app, you want to have a long-term relationship with them, and app crashes can become a deal-breaker. What if you had a chance to say “I’m sorry” in a very personal way? Such as your stats show that 30% of your users experiencing a crash with the latest release, and you just need to tell them to update in order to deliver a fix. However, other 70% might not be happy hearing about possible issues with the app they love so much. Here’s where we step in to assist. Crashlytics and Pushwoosh integration is very simple. With just a few lines of code you would be able to target and notify only those users who experienced particular crash in the app, without having to bother the rest of your audience. Let’s see how it works. ## Integrating Crashlytics First of all, we have to feed some identifier to [Crashlytics](https://firebase.google.com/docs/crashlytics) that will help us to target user later. Crashlytics provides **setUserIdentifier** method for this purpose (link to [iOS](https://docs.fabric.io/ios/crashlytics/enhanced-reports.html#user-identifiers), link to [Android](https://docs.fabric.io/android/crashlytics/enhanced-reports.html#user-information)). In our case we need to set it to Pushwoosh Hardware ID. As you may know Pushwoosh Hardware ID is *IDFV* (or *IDFA* if you link to *AdSupport.framework*) on iOS and in most of the cases *Android Id* or *Serial Number* on Android. This code makes it simple: * Java ```java String userId = pushManager.getPushwooshHWID(this); Crashlytics.setUserIdentifier(userId); ``` * Objective-C ```objective-c NSString * userId = [[PushNotificationManager pushManager] getHWID]; [Crashlytics setUserIdentifier:userId]; ``` That’s it! Now you are collecting user IDs with Crashlytics. ## Downloading User IDs from the crash reports in Crashlytics Please note that it may take up to 24 hours as per Fabric documentation for the “Export IDs” button to appear on the crash detail report in the Crashlytics dashboard. ![Export IDs button in Crashlytics crash detail report](https://docs.pushwoosh.com/integrations-crashlytics-integration-1.webp) Click on the *Export IDs* button will download *user-data.csv* file. The file has the following structure: user-data.csv ```javascript userId,email,name,mostRecentTimestamp,count XXXXXXXXXXXXXXXX,test@example.com,John Snow,MAR 03 2016 01:22,1 YYYYYYYYYYYYYYYY,another@example.com,John Snow Jr,MAR 03 2016 01:11,1 ``` --- # Event streaming integration **URL:** https://docs.pushwoosh.com/product/integrations/event-streaming/ Stream communication event data in real-time via HTTP or gRPC with advanced filtering for analytics, automation, and reporting. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. ## Integration overview ### Integration type **Source:** Data is sent from Pushwoosh to your system via HTTP or gRPC based on configured event triggers. ### How does the integration work? Pushwoosh transmits communication event data (e.g., push/email activity) to a client-defined endpoint. Data is sent in batch streams at scheduled intervals or upon reaching a minimum batch size. Data is only sent if it matches the selected events, platforms, and optional filters (campaign/message codes, live activity). The client endpoint must be ready to receive and optionally respond with a status. ### Glossary **Endpoint URL**: Server-side endpoint which allows receiving requests. The client may specify a port if needed. Examples: * `https://clientdomainname.com/webhook_endpoint` * `https://clientdomainname.com:8081/webhook_endpoint` ### List of synchronized entities * Communication statistics events (e.g., Push Sent, Email Delivered) * [Post events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/): events your app sends to Pushwoosh via `postEvent`, streamed separately from communication statistics events ### Use cases * **Real-time engagement tracking** Monitor user interactions such as push sent, email opened, or message delivered as they happen, enabling immediate visibility into campaign performance. * **External analytics integration** Stream events to third-party analytics platforms for centralized reporting and analysis. * **Automated user workflows** Trigger actions in external systems (like CRMs or marketing automation tools) based on user behaviors, e.g., send a follow-up message when a user opens an email. * **Device list sync** Remove devices from your own database when they can no longer receive messages. See [Sync unregistered devices to your system](https://docs.pushwoosh.com/product/integrations/event-streaming/#sync-unregistered-devices-to-your-system). ## Setting up the integration To set up the integration: 1. In your Pushwoosh account, go to **Settings > 3rd party Integrations**, find **Event streaming integration**, and click **Configure**. ![Configure Event streaming integration](https://docs.pushwoosh.com/integrations-event-streaming-integration-1.webp) 2. In the window that opens, fill out the necessary fields. ![Fill out the necessary fields](https://docs.pushwoosh.com/integrations-event-streaming-integration-2.webp) #### Enter endpoint URL In the **Endpoint URL** field, enter the full URL where events will be sent, including the protocol and port if applicable. **Example** * `https://clientdomainname.com/webhook_endpoint` * `https://clientdomainname.com:8081/webhook\_endpoint` #### Select events From the **Events** dropdown, select at least one event. If none are selected, validation will fail. The list of events is managed by the backend and may change over time. #### Provide authorization credentials If required by your server, enter the complete value for the `Authorization` header in the **Authorization** field. Examples: * `Bearer your_token_here` * `Basic base64encoded_credentials` Note The value is inserted **as-is** into the `Authorization` header (HTTP) or gRPC metadata. Ensure there is a space between the auth scheme and the token. #### Choose transport type From the **Transport type** dropdown, choose the delivery protocol for event transmission: **HTTP** or **gRPC**. Each has specific behavior and configuration. ##### HTTP With the **HTTP** transport type, Pushwoosh collects events into a batch and sends it when either of the following conditions is met: * 10,000 events have been collected, or * 5 minutes have passed since the batch was started. A batch is delivered in HTTP requests of up to 100 events or 768 KiB, whichever limit is reached first. After sending the data, the connection is closed once a successful response is received. If the server responds with a `5xx`, `408`, or `429` status code, Pushwoosh retries the request according to the defined retry policy. **Retry mechanism** | Attempt | Delay | | :------ | :---------------------- | | 1st | 1 second | | 2nd | 3 seconds after 1st try | | 3rd | 8 seconds after 2nd try | If all retries fail, Pushwoosh keeps the events that weren’t delivered and tries them again a minute later. Events your endpoint already accepted aren’t sent again. If your endpoint responds with `400`, `413`, or `422`, Pushwoosh splits the request into smaller ones until the refused event is sent alone, then drops that event. Any other `4xx` response stops delivery of the batch: Pushwoosh retries it for up to 2 hours, then drops it. If your endpoint keeps failing for 24 hours without accepting any events, Pushwoosh pauses the integration. See [Paused integration](https://docs.pushwoosh.com/product/integrations/event-streaming/#paused-integration). **Timeout** The default timeout for a request is 60 seconds. This can be customized upon request via support. [View example ](https://docs.pushwoosh.com/webhook_request_batch.json) ##### gRPC The gRPC transport type uses **bidirectional streaming** for data transmission. Learn more in the [gRPC documentation](https://grpc.io/docs/what-is-grpc/core-concepts/#bidirectional-streaming-rpc). A stream is opened when either of the following conditions is met: * At least 1,000 events are ready for delivery * One hour has passed since the last stream was opened The stream is closed after the events are sent. This ensures that a new stream is not opened for each individual event in a short time. [See protobuf specification ](https://docs.pushwoosh.com/webhook.proto) **Retry mechanism**\ Each event includes a unique `uuid`. If an event fails: 1. The response must include a `status` **not equal to** `"Success"` 2. The original `uuid` from the request must be included Pushwoosh will retry delivery based on this response. **Connection settings** Advanced options like **TLS**, **keep-alive**, or **retry policies** are configured manually via support and may require development involvement. #### Enable post events This toggle is off by default, so only communication statistics events stream. To also stream [events your app sends to Pushwoosh via `postEvent`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/), turn on the **Allow post events** toggle. From the **Post events** field that appears, select which of your account’s events to stream. At least one event is required while the toggle is on. If your account hasn’t sent any events yet, this list is empty until you call `postEvent` at least once. Note Post events are sent to the same **Endpoint URL** entered above, and reach it regardless of platform: the **Platforms** and **Advanced filters** sections below apply only to communication statistics events, not to post events. In-app impressions and clicks also travel through this post event stream, as `PW_InAppShow` and `PW_InAppClicked`. Each appears in the **Post events** list only after it has fired at least once for the account, same as any other post event. ### Select platforms In the **Platforms section**, select at least one platform to activate event streaming. ![Select at least one platform](https://docs.pushwoosh.com/integrations-event-streaming-integration-3.webp) Supported platforms include: * iOS, Android, macOS, Windows, Amazon, Safari * Chrome, Firefox, Internet Explorer, Huawei * Email, SMS, Line, Xiaomi, WhatsApp ### Configure advanced filters In the **Advanced filters** section, refine the event delivery criteria using filters: * **Live activity events:** Enable to receive live activity events. These events contain only metadata including `live_activity_id`. * **Campaign filters:** Filter by campaign code. Only events tied to these campaigns will be delivered. * **Message filters:** Filter by message code. Only events tied to these messages will be delivered. ![Set advanced filters](https://docs.pushwoosh.com/integrations-event-streaming-integration-4.webp) After completing all the required fields, click the **Apply** button to save and activate your integration. Note Configuration changes will take effect **within 15 minutes** after submission. Tip For advanced settings such as custom timeouts or gRPC configurations, please [contact support](https://help.pushwoosh.com/hc/en-us/requests/new). ## Request details and example | | | | ------------------- | ---------------------------------------------------- | | **Endpoint** | `https://exampleclientendpoint.com/webhook_endpoint` | | **HTTP Request** | `POST` | | **Authentication** | No | | **Request type** | Source | | **Request meaning** | Send requests to the webhook endpoint | | **Headers** | `Content-Type: application/json` | **Request body example** ```plaintext { "event_name": "Email Opened", "message_code": "E682-E6D92B9A-53E24868", "campaign_id": 961048, "platform": "Email", "payload": "Welcome to Headway! 👋", "application_code": "XXXXX-XXXXX", "hwid": "user@example.com", "user_id": "USER_ID", "timestamp": 1723799271, "journey_title": "", "journey_point_title": "5_Welcome_ID_new", "attributes": null } ``` ### Attributes Among communication statistics events, `attributes` populates only for push events, with channel-specific content (Android, iOS, and so on) for the platform that push was sent to. Email and SMS events carry only the top-level fields (`event_name`, `platform`, `payload`, and so on). `attributes` is `null` for those events. Within a populated `attributes` object, fields outside the event’s own platform are omitted rather than sent as empty values. See [`webhook.proto`](https://docs.pushwoosh.com/webhook.proto) for the full field list. A post event carries its attribute key/value pairs in `attributes.event_attributes` instead. `message_code` and `payload` stay empty, because a post event isn’t tied to a message or campaign. Caution `attributes.geozone` is deprecated: the source property is an object, not a number, so this field never carried a real value. Read the actual geozone from `attributes.geozone_data` instead. The old field stays in the contract for clients with existing gRPC stubs. **Response**\ The response body is ignored. With the HTTP transport, the response code decides whether the events count as delivered. A `2xx` code means your endpoint accepted them. ## Sync unregistered devices to your system If you keep your own copy of the device list, for example in a CRM or a customer data platform, stream the `PW_DeviceUnregistered` post event to find devices that Pushwoosh can no longer reach. Pushwoosh sends this event when a device loses its push subscription. For the reasons, see [PW_DeviceUnregistered](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceunregistered). Start from an integration that is already [set up](https://docs.pushwoosh.com/product/integrations/event-streaming/#setting-up-the-integration), with an **Endpoint URL** entered and at least one platform selected. Then: 1. In the integration settings, turn on **Allow post events**. See [Enable post events](https://docs.pushwoosh.com/product/integrations/event-streaming/#enable-post-events). 2. In the **Post events** field, select `PW_DeviceUnregistered`. 3. To also learn about devices that get a push token, select `PW_DeviceRegistered`. 4. Click **Apply**. The change takes effect within 15 minutes. Like any post event, `PW_DeviceUnregistered` appears in the **Post events** list only after it has fired at least once for the account. See [Enable post events](https://docs.pushwoosh.com/product/integrations/event-streaming/#enable-post-events). Keep these limits in mind when you process the events: * **Match devices by `hwid` or `user_id`, not by push token.** The event carries the device’s `hwid`, `user_id`, and platform, but no push token and no reason for the unregistration. An expired token and an SDK unregistration look the same. * **A new token for the same device sends no event.** `PW_DeviceRegistered` fires only when a device without a push token receives one. A list that stores devices by push token keeps tokens that no longer work, because when a device gets a new token, neither event fires. * **An email unsubscribe doesn’t delete the device.** For email, the address stays on the device and Pushwoosh only marks it as unsubscribed. When the user unsubscribed through a link in an email, `attributes.event_attributes` also carries the `message_code` and `campaign_id` of that email. Some events may never reach your endpoint: an event your endpoint refuses with `400`, `413`, or `422` is dropped, and so is a batch your endpoint keeps refusing with another `4xx` code for 2 hours (see [HTTP](https://docs.pushwoosh.com/product/integrations/event-streaming/#http)). To catch these devices, periodically [export the segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#export-segment-users) of devices you target to CSV and compare it with your device list. Exporting a segment requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. If your endpoint keeps failing for 24 hours, the integration is paused and events stop arriving. See [Paused integration](https://docs.pushwoosh.com/product/integrations/event-streaming/#paused-integration). ## Paused integration If deliveries to your HTTP endpoint keep failing for 24 hours and the endpoint accepts no events during that time, Pushwoosh pauses the integration: * Pushwoosh stops sending events to the endpoint. * Events waiting to be sent are discarded. Events from the paused period aren’t sent later. * The [account owner](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#users) receives an email about the pause. The integration settings don’t show the pause, so this email is the only notice. An event counts as accepted when your endpoint returns a `2xx` response code. Partial failures don’t pause the integration: any accepted event restarts the 24-hour count. Integrations with the gRPC transport aren’t paused. To resume streaming: 1. Make sure your endpoint is reachable and returns a `2xx` response code. 2. Go to **Settings > 3rd party Integrations**, find **Event streaming integration**, and click **Configure**. 3. Click **Apply**. You don’t need to change any settings. Streaming resumes within 15 minutes. ## How do you know that the integration is working? You will start receiving requests from Pushwoosh at your configured endpoint. --- # Figma integration **URL:** https://docs.pushwoosh.com/product/integrations/figma-integration/ Send Figma frames to the Pushwoosh Media storage with the Pushwoosh plugin in PNG or JPG and re-send them as new versions. --- The [Pushwoosh plugin](https://www.figma.com/community/plugin/1690328126861361607) for [Figma](https://www.figma.com/) sends frames from your design file straight to the [Media storage](https://docs.pushwoosh.com/product/content/media-store/) of a Pushwoosh application. Push images, in-app banners, and email headers go from Figma to your content without exporting, downloading, and re-uploading files. The plugin works in the Figma editor and in Dev Mode (the Figma mode where developers inspect designs). ## Integration overview ### Integration type **Destination:** Images exported from Figma are sent to the Pushwoosh Media storage. ### Prerequisites * A Pushwoosh account. If the plugin isn’t available for your account, contact your Customer Success Manager or [Pushwoosh Support](https://www.pushwoosh.com/contact-us/). * Access to the Pushwoosh application you want to send images to. The plugin lists only the applications you can open in the Control Panel. * A Figma account and the [Pushwoosh plugin](https://www.figma.com/community/plugin/1690328126861361607) from Figma Community. If you don’t have a Pushwoosh account, click **Create an account** on the plugin’s sign-in screen. The Pushwoosh registration page opens in your browser. After you register, return to the plugin and sign in. ### How does the integration work? Each Pushwoosh user connects the plugin with their own login. You start signing in from the plugin, and it shows a short code. You enter that code in the Pushwoosh Control Panel and allow access. From then on, the plugin can do two things on your behalf: * see the list of your applications * upload images to the Media storage of those applications The plugin never gets your Pushwoosh password, and it can’t read or change anything else in your account. ## Connect the plugin Caution Never enter a code that someone else sent you. Allowing it connects their plugin to your login, and they can upload images to your applications. 1. Open the [Pushwoosh plugin](https://www.figma.com/community/plugin/1690328126861361607) in Figma Community and run it in your design file. The plugin opens on the sign-in screen. The **How it works** link under the button opens this page. ![Pushwoosh plugin sign-in screen with the plugin description, Europe (app.pushwoosh.com) in Region, the Sign in to Pushwoosh button, and the Create an account and How it works links](https://docs.pushwoosh.com/integrations-figma-integration-5.webp) 2. In **Region**, select **Europe (app.pushwoosh.com)**. 3. Click **Sign in to Pushwoosh**. The plugin shows a code that works for 10 minutes. 4. Click **Open Pushwoosh**. The Control Panel opens on the **3rd-party integrations** page with the code form already open. If the form doesn’t open, for example because you haven’t used the Control Panel in this browser before, go to **Settings > 3rd-party integrations** and click **Enter code** on the **Figma** card. ![Figma card in the Available services section of 3rd-party integrations with the Enter code link](https://docs.pushwoosh.com/integrations-figma-integration-1.webp) 5. Enter the code from the plugin and click **Continue**. 6. Check the details of the sign-in request: the account and user the plugin connects to, when the sign-in started, the IP address, and the device. Click **Allow** only if you started signing in from the plugin yourself. ![Connect Figma to Pushwoosh window listing what the plugin will be able to do, the sign-in details, and the Cancel and Allow buttons](https://docs.pushwoosh.com/integrations-figma-integration-2.webp) 7. Return to Figma. The plugin signs in on its own and shows your account name and email. If the code doesn’t work: * If it expires before you click **Allow**, the plugin says so. Click **Sign in to Pushwoosh** again to get a new code. * After five wrong codes, the Control Panel stops accepting codes for up to 10 minutes. Wait, then click **Sign in to Pushwoosh** in the plugin to get a new code. ## Send frames to the Media storage 1. On the Figma canvas, select one or more frames. You can also select other layers, such as groups or components. 2. In **Application**, select the application to send the images to. If the list is empty, you don’t have access to any application yet. Ask an account admin to give you access. 3. In **Format**, select **PNG** or **JPG**. 4. In **Scale**, select **1×**, **2×**, or **3×**. Each selected frame is listed with its size in Figma and the size of the exported image, for example `720×360 → 1440×720`. 5. Click **Send**. The button shows how many frames will be sent, for example **Send 3 frames**. ![Pushwoosh plugin in Figma with an application selected, PNG at 2x, three selected frames listed as 720×360 → 1440×720, and the Send 3 frames button](https://docs.pushwoosh.com/integrations-figma-integration-4.webp) When the upload finishes, every frame appears in the [Media storage](https://docs.pushwoosh.com/product/content/media-store/) of the selected application. To paste an image into your content right away, click **Copy URL** next to the frame in the plugin. ### Image limits The Media storage accepts images up to 25 megapixels and 10 MB. The plugin checks both before sending: * **25 megapixels:** the limit applies to the exported image after scaling. A frame that would be larger is marked in the list, for example `over 25 MP, use 2×`, and is not sent. Select a smaller scale to send it. * **10 MB:** a PNG larger than 10 MB is sent as JPG instead. A JPG larger than 10 MB is not sent. JPG has no transparency, so transparent areas of a frame become white. ### Send an updated frame When you change a frame and send it again to the same application, the plugin uploads it as a new version of the image it sent before. The button shows this, for example **Send 3 frames (1 as new version)**, and the frame is marked `new version` in the list. The Media storage shows the new version instead of the previous one. The new version has its own URL, so copy it again and update the content where the image should change. Deleting the image in the Media storage removes only its latest version, and the previous version takes its place. See [Delete an original image](https://docs.pushwoosh.com/product/content/media-store/#delete-an-original-image). A frame is sent as a new image, not a new version, when: * you send a copy of the frame, or the same frame from another Figma file * you send it to another application * the previous image was deleted from the Media storage ## Manage connections Each sign-in from the plugin is a separate connection under your login. When at least one connection exists, the **Figma** card moves to **Connected services** and shows how many devices are connected. To see or remove your connections: 1. Go to **Settings > 3rd-party integrations**. 2. On the **Figma** card, click **Manage**. 3. Review the list. Each connection shows when it was connected, when it was last used, when it expires, and its IP address. ![Figma plugin connections window with one connection showing its connection date, last use, expiry, and the Disconnect button](https://docs.pushwoosh.com/integrations-figma-integration-3.webp) 4. Click **Disconnect** next to a connection you no longer use. The plugin on that device stops working at once and has to sign in again. A connection expires after 30 days without use, and in any case 90 days after you allowed it. After that, sign in from the plugin again. Note **Sign out** in the plugin only signs out that copy of the plugin. The connection stays in the list until it expires. To end access right away, click **Disconnect** in the Control Panel. ## FAQ ### Can my teammates send images from my connection? No. Every teammate connects the plugin with their own Pushwoosh login, and each connection sees only the applications that person can access. Disconnecting a connection doesn’t affect anyone else’s. ### Which formats and sizes can I send? PNG or JPG, exported at 1×, 2×, or 3×. Each image can be up to 25 megapixels after scaling and up to 10 MB. --- # Flight Status integration **URL:** https://docs.pushwoosh.com/product/integrations/flight-status-integration/ Connect AeroDataBox to Pushwoosh and notify passengers through customer journeys when a flight's gate or status changes. --- Tell passengers about changes to their flight as they happen: a new gate, a delay, boarding, arrival, or a cancellation. The Flight Status integration connects Pushwoosh to [AeroDataBox](https://www.aerodatabox.com/), a flight-data provider, so a [customer journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) can watch a specific passenger’s flight and react the moment its status changes. Developer assistance needed You’ll need your development team to send the booking event with the right attributes and to build the Webhook step’s request body. Share this guide with them. ## Integration overview ### Integration type **Source:** you subscribe a booking to its flight from inside a journey. Pushwoosh sends the status changes back as an event you use later in that same journey. ### Prerequisites Before connecting Flight Status, make sure you have: * An active Pushwoosh account with an application in Pushwoosh’s NUE data center. Flight Status isn’t available in other data centers yet. * An AeroDataBox account and API key. The feed is billed on your own AeroDataBox account. * A booking event that carries the flight’s carrier, number, date, and departure airport (see [Build the flight status journey](https://docs.pushwoosh.com/product/integrations/flight-status-integration/#build-the-flight-status-journey)). * A dedicated [API Access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) for the journey to authenticate with. ### How does the integration work? Connecting the integration and watching one flight are two separate steps, done at different times: 1. Connect your AeroDataBox key in **Settings** → **3rd-party integrations**. 2. A booking event enters a passenger into your journey. 3. The journey’s Webhook step subscribes that booking to its flight through Pushwoosh’s public API. 4. Pushwoosh watches the flight with AeroDataBox and detects changes: gate, delay, boarding, arrival, cancellation, or a baggage belt assignment. 5. Each change is delivered to the app as a `PW_FlightStatusChanged` event, which the journey’s [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) and [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) route to the right message. Each flight subscription ends automatically 36 hours after the local departure date. It can end sooner: once the flight lands or is cancelled, or once nothing is watching it anymore. Pushwoosh then cancels the matching AeroDataBox subscription, so it doesn’t keep billing in the background. This window is fixed at subscribe time, from the booked departure date, and doesn’t shift if AeroDataBox later reports a delay. A delay that pushes the flight into the next calendar day can make the subscription end before the delayed actual departure. ### Use cases Flight Status covers four kinds of updates, each usable on its own or combined in one journey: * **Gate change alerts:** notify passengers the moment their departure gate changes. * **Delay notifications:** alert passengers once a flight’s delay passes a few minutes, so they can adjust their plans. * **Boarding and arrival updates:** tell passengers when boarding opens or their flight lands. * **Baggage claim:** send the baggage belt number as soon as it’s assigned. ## Set up the integration ### Connect Flight Status to Pushwoosh Connect your AeroDataBox key once per application: 1. Open your application and go to **Settings** → **3rd-party integrations**. 2. Under **Available services**, find the **Flight Status** card and click **Configure**. ![Flight Status card in the 3rd-party integrations list, showing its description and Configure button](https://docs.pushwoosh.com/integrations-flight-status-integration-2.webp) 3. Paste your AeroDataBox key into **API key** and click **Connect**. ![Flight Status Configure dialog with Provider set to AeroDataBox and an empty API key field](https://docs.pushwoosh.com/integrations-flight-status-integration-1.webp) After you click **Connect**, the card moves to **Connected services**. ### If the key is rejected Pushwoosh checks the key in the background. If something’s wrong, the card shows one of these messages: | Message | Cause | | ------------------------------------ | ----------------------------------------------------------------------------------- | | `provider rejected the API key` | The key is invalid or was revoked in AeroDataBox | | `provider account is out of credits` | Your AeroDataBox plan has run out of credits | | `provider rate limit reached` | AeroDataBox is throttling requests, and this clears on its own | | `provider is unavailable` | AeroDataBox couldn’t be reached, due to a network issue or an outage on either side | | `provider refused the request` | AeroDataBox returned an error Pushwoosh doesn’t otherwise recognize | ### Replace the key Reopen the **Flight Status** card in **Connected services**, for example after the key is rejected: * **Replace the key:** paste a new one into **API key**. * **Keep the current key:** leave **API key** empty. The field shows only the last few characters of the saved key. ### Disconnect the integration 1. Open the **Flight Status** card in **Connected services**. 2. Remove the key. After you disconnect: * New subscriptions are no longer created. * Flights that journeys already watch keep their subscriptions until they [end on their own](https://docs.pushwoosh.com/product/integrations/flight-status-integration/#how-does-the-integration-work) or you delete them from the journey. * The active-subscriptions count on the card includes these subscriptions until they end. ## Build the flight status journey ### Before you build the journey Make sure you have: * A booking event carrying the flight’s carrier, number, date (`YYYY-MM-DD`), and departure airport, plus one attribute holding the flight key in `//` format, for example `LH400/2026-09-20/MUC`. This is what session matching uses throughout the journey. * A dedicated [API Access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/). The subscribe method accepts any token from your account, with no permissions to grant. Create one specifically for this journey so you can revoke it later without touching anything else. * Your data center’s public API host. For NUE accounts, that’s `rpc-api.svc-nue.pushwoosh.com`. * The journey’s [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), turned off. Campaign entry limit tracks entries per user only. It doesn’t know about the session identifier you set up below, so it would block a passenger’s second flight until the limit period passes. ### Start the journey from a booking event 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) and select your booking event, for example `flight_booked`. 2. Under **Control how many sessions a user can have at the same time**, choose **Multiple active sessions per user**. 3. Pick the flight-key attribute as the session identifier. This lets the same passenger track more than one flight at once, each in its own session. ### Subscribe the booking with a Webhook step Add a [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) step directly after the entry. Its request body pulls the flight fields from the entry event, so the step needs to sit right after entry to use them. 1. Set **REQUEST TYPE** to `POST`. 2. Set **URL** to `https://rpc-api.svc-nue.pushwoosh.com/api/integrations/flight-status/subscriptions`. 3. In **HEADERS**, keep `Content-Type: application/json`. 4. Add a header `Authorization: Token `. Pushwoosh masks this value after you save, because any header named `Authorization` is treated as a secret automatically. See [Mark a header value as secret](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#mark-a-header-value-as-secret) for what that means for editing and version history. 5. In **DATA**, enter the request body below, typing your own application code directly: ```json { "application": "", "user_id": "{{device:user_id}}", "source": "journey", "flight": { "carrier": "", "flight_number": "", "flight_date": "", "departure_airport": "" } } ``` 6. For each of the four empty `flight` values, open **DATA BUILDER**. 7. Select category **Event**. 8. Pick the matching attribute from your booking event (carrier, flight number, flight date, departure airport). 9. Copy the macro Pushwoosh generates and paste it in as that field’s value. Repeat for the remaining three values. You don’t need to map anything from the response. It returns `flight_key`, which is already on your booking event. Caution If the subscribe call fails, the passenger leaves the journey at this step, the same as with any Webhook step. Their flight isn’t watched, and they won’t get any of the alerts further down the journey. See [What happens when a request fails](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#what-happens-when-a-request-fails). ### Wait for a status update Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step after the Webhook step. 1. Add one branch and set its event to `PW_FlightStatusChanged`. 2. Under multi-session attribute matching, select the same flight-key attribute you used on the entry. This makes sure a status update only wakes the passenger whose flight it’s actually about. 3. Set the waiting period to comfortably cover the flight. 48 hours is enough for most itineraries. 4. Leave the **Not triggered** branch without a next step, or add a fallback message. Passengers whose flight has no update before the wait ends leave the journey here, and that’s expected. ### Branch by event type Add a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) after the Wait for Trigger step. 1. Select **Event** as the condition type. 2. In **Event from Journey**, choose `PW_FlightStatusChanged`. 3. Under **Attribute**, select `event_type`. 4. Set the condition to **is**. 5. Add a branch with the value `gate_change`. 6. Click **Save**. This creates two branches: the one you named for a gate change, and **All other users** for every other event type. Repeat this element, or add more branches to it, for the other `event_type` values you want to act on: `delay`, `boarding`, `departed`, `arrived`, `cancelled`, and `baggage_ready` all work the same way. ### Notify the passenger Add a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element on the gate-change branch. 1. Select or create a push preset. 2. Set **Message type** to **Transactional message**, since a flight status alert is a service notification, not a promo. Frequency capping doesn’t apply, and it still reaches passengers in a [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). 3. Enable [personalization with event attributes](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/#dynamic-content-personalization). 4. Choose `PW_FlightStatusChanged` as the source event. 5. Fill your preset’s placeholders from `flight_number` and `gate_new`. ### Show a Live Activity card instead Add three [Live Activity](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) elements, in place of Push or in addition to it: * **Start:** right after the Webhook step, not directly after the entry. The entry connects to only one next step, so Webhook and Start can’t both come right after it. * **Update:** on the gate-change branch. * **End:** once the journey no longer needs to track the flight, for example after arrival or cancellation. On the **Start** element, under **Card attributes**, add all six fields the card’s `ActivityAttributes` type needs. Card attributes is a free list of names and values, and the interface doesn’t check the names, so enter each one exactly as listed. Five of them are already on your booking event: * `carrier` * `flight_number` * `flight_date` * `departure_airport` * `flight_key` * `arrival_airport`: the subscribe call doesn’t need it, so add it to your booking event only if you use Live Activity. Caution If any of the six Card attributes is missing, the interface shows no error, but the card never appears on the device. Every later **Update** or **End** for that passenger then fails with delivery code `1031` (“push token not found for the specified Live Activity ID”). Only **Start** sets Card attributes, and they stay the same for the card’s whole life. **Update** and **End** don’t set them. The fields that change, such as status, gate, and delay, are **Card content**, and they come from the widget schema you [publish for this app](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-live-activity-schemas-configuration/). ## PW_FlightStatusChanged event reference Every change the integration detects is delivered as one `PW_FlightStatusChanged` event, with all attributes always present: empty ones are sent as blank values, never left out. | Attribute | Type | Description | | --------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------------- | | `event_type` | String | What changed (see the values below) | | `flight_key` | String | The same flight key you set on the booking event | | `flight_number` | String | The flight number | | `departure_airport` | String | Departure airport code | | `arrival_airport` | String | Arrival airport code | | `status` | String | Current flight status (see the values below) | | `gate_old` / `gate_new` | String | Departure gate before and after the change | | `terminal_old` / `terminal_new` | String | Departure terminal before and after the change | | `baggage_claim` | String | Baggage belt number, once assigned | | `provider` | String | The data provider that reported the change (`aerodatabox`) | | `delay_minutes` | Integer | Minutes running late against schedule, present on every event | | `scheduled_at` / `estimated_at` / `actual_at` | String | Scheduled, currently estimated, and actual departure times, in the provider’s own format | | `arrival_terminal` | String | Arrival terminal, once assigned | | `arrival_scheduled_at` / `arrival_estimated_at` / `arrival_actual_at` | String | Scheduled, currently estimated, and actual arrival times, in the provider’s own format | | `scheduled_at_local` / `estimated_at_local` / `actual_at_local` | String | The three departure times above, in the departure airport’s local time | | `arrival_scheduled_at_local` / `arrival_estimated_at_local` / `arrival_actual_at_local` | String | The three arrival times above, in the arrival airport’s local time | | `flight_date` / `event_time` | Date | The flight’s date, and when the change happened | ### Attribute values and formats * **`event_type` values:** `gate_change`, `delay`, `boarding`, `departed`, `arrived`, `cancelled`, `baggage_ready`. * **`status` values:** `scheduled`, `check_in`, `boarding`, `departed`, `delayed`, `arrived`, `cancelled`, `diverted`, `unknown`. An AeroDataBox status that Pushwoosh doesn’t recognize is reported as `unknown`. * **`delay_minutes`:** present on every event, not only `delay` ones. 0 means the flight is on time, and a negative value means it’s running early. A `delay` event is sent once the delay reaches 5 minutes. * **Time attributes:** all of them, including the `arrival_*` and `_local` ones, are String, not Date. This way an empty time isn’t dropped from the event, and a local time keeps the airport’s UTC offset. To filter by date, use `flight_date` and `event_time`. --- # Google Ads integration **URL:** https://docs.pushwoosh.com/product/integrations/google-ads-integration/ Connect Pushwoosh to Google Ads. Sync Journey Builder audiences to Google Customer Match audiences for targeting or suppression. --- [Google Ads](https://ads.google.com/) integration lets you sync Pushwoosh audiences to a Google Ads account as Customer Match audiences. Use it to target or exclude users in ad campaigns and add paid ads as another channel in your customer journey. ## Use cases Use this integration to: * target high-value users in multiple channels to increase purchases or engagement * retarget users who are less responsive in other channels * build suppression audiences so loyal customers do not receive unnecessary ads ## Prerequisites Before you connect Google Ads, make sure: * You have the **Admin** role in your Pushwoosh account. See [Manage user access and permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#creating-and-managing-roles-ex-groups) for how roles and permissions work. * You have a [**Google Ads account**](https://ads.google.com/) with Customer Match available. * Your Google Ads account has [Customer Match](https://support.google.com/google-ads/answer/6379332) enabled. Google enables it per account after a manual review, so a newly created account may not have it yet. Caution When you connect Google Ads, Google may show a **Google hasn’t verified this app** warning. You can continue past it. If you connected Google Ads earlier and audience uploads fail after you launch a journey, reconnect the account once so Pushwoosh gets the Data Manager permission Google needs for Customer Match uploads. See [Reconnect if audience uploads fail](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#reconnect-if-audience-uploads-fail). An older connection cannot upload audiences until you reconnect. ## Set up Google Ads in Pushwoosh 1. In Pushwoosh, go to **Settings** > **3rd party integrations**. 2. In the Google Ads card, click **Connect**. ![Settings menu with 3rd party integrations highlighted, and the Google Ads card showing Connect to start linking a Google Ads account](https://docs.pushwoosh.com/integrations-google-ads-integration-1.webp) 3. Sign in to your Google account, then allow the permissions Pushwoosh requests. Leave the Data Manager permission on alongside Google Ads. Without it, Customer Match uploads will not work. 4. Select the ad accounts you want to connect. Choose the client ad account where the Customer Match audience will live. Connecting only the manager (MCC) account is not enough. Google does not keep Customer Match audiences on manager accounts, so you will not have an audience to sync to. Note This list only shows ad accounts the Google user you signed in with can open directly. If you only manage an account through a Google Ads manager account (MCC), it may not appear here. Sign in with a user who has direct access to that client account, or ask your Google Ads admin. ### Review connection status After setup, you stay on the **3rd party integrations** page, and the Google Ads card shows as connected. Click **Settings** on the card to open the **Google Ads** page in Pushwoosh. ![Google Ads page in Pushwoosh with connected ad accounts table showing name, ID, and row menu actions](https://docs.pushwoosh.com/integrations-google-ads-integration-2.webp) Each connected ad account shows its name and ID. Open the three dots at the end of a row and choose: * **Create audience:** add a Customer Match audience (see [Manage Customer Match audiences](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#manage-customer-match-audiences)) * **Remove ad account:** delete that ad account from the list in Pushwoosh ### Reconnect if audience uploads fail If you connected Google Ads before Pushwoosh started using Google’s Data Manager permission for Customer Match uploads, that older connection cannot upload audiences. Reconnect it once: 1. On the **Google Ads** page, click **Connect** again (or repeat [Set up Google Ads in Pushwoosh](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#set-up-google-ads-in-pushwoosh) from step 2). 2. Sign in with the same Google user and grant the Data Manager permission on the consent screen. You only need to do this once per connected Google account. Every ad account connected under it picks up the Data Manager permission. Note Skipping reconnect does not block a journey from launching. What happens next: * The first time an **Audience sync** point tries to upload users, the upload fails. * Pushwoosh pauses that journey version the same way it does for a disconnected ad account. See [Behavior and error handling](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#behavior-and-error-handling). * In the point’s statistics, these travelers use a separate audience-sync-failure status, not the disconnected-account status, so you can tell the two apart. * If the failure reason mentions missing Data Manager permission or `datamanager` access (Google may quote: `connection does not grant the requested scopes: https://www.googleapis.com/auth/datamanager`), reconnect as above, then check **Audience sync** statistics again. ### Manage Customer Match audiences A journey’s **Audience sync** point needs an existing Customer Match audience to target. Pushwoosh does not create one for you automatically. 1. On the **Google Ads** page, click the expand arrow on an ad account row to open **Customer Match audiences**. 2. Click **Create audience** (also available from the row’s three dots). Either path opens the same create flow. 3. Enter an **Audience name**. **Description** is optional. 4. Click **Create**. ![Create Customer Match audience dialog with Audience name and Description filled in, and the Create button](https://docs.pushwoosh.com/integrations-google-ads-integration-3.webp) This creates an empty Customer Match audience on that Google Ads account. Journeys with an **Audience sync** point upload matching users into it. In **Customer Match audiences**, check each audience’s name, ID, and size. ![Customer Match audiences table showing audience name, audience ID, and size](https://docs.pushwoosh.com/integrations-google-ads-integration-4.webp) Note Size is Google’s own estimate. It stays 0 until Google matches the uploaded users, which can take up to 48 hours. A 0 does not mean the audience is empty. To delete an audience, click the trash icon on its row, then click **Delete** to confirm. Caution Deleting an audience cannot be undone. It removes the audience from Google Ads, and every journey with an **Audience sync** point targeting it stops working. ### Manage connected ad accounts 1. On the **Google Ads** page, click **Manage accounts**. 2. Use the toggle on each row to include or exclude that ad account from the integration. 3. Click **Apply** to save changes, or **Cancel** to close without saving. ![Manage accounts dialog with row toggles to include or exclude Google Ads accounts, Apply and Cancel](https://docs.pushwoosh.com/integrations-google-ads-integration-5.webp) Note Google Ads has no separate field-mapping step. Google can match a user only if their Pushwoosh profile has at least one of: email, phone number, or mobile advertising ID (**MADID**). Pushwoosh hashes email and phone before sending them. MADID goes to Google as is. If a profile has none of these, that user is skipped at **Audience sync** and continues the journey. ## Sync audiences in journeys The **Audience sync** point in **Journey Builder** links your journey to a Google Ads Customer Match audience. Every time a user reaches that point, Pushwoosh asks Google to either add them to the audience or remove them from it. For example, you may use this to stop showing an ad to users who already converted, so you do not waste ad spend on people who do not need to see it anymore. To configure audience sync: 1. Open [**Journey Builder**](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). 2. Add an [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). In **Audience source**, choose a Pushwoosh segment or list that defines who enters this journey. For example, a segment of users who already purchased. Only those users move through the journey and reach **Audience sync**. 3. Add the **Audience sync** point. 4. Under **How to sync users info to the ad audience**, pick one option: * **Add users to audience**. Adds each user who reaches this step to the audience you select. For example, use this to start showing an ad to users who browsed but have not purchased. * **Remove users from audience**. Removes each user who reaches this step from that audience. In the suppression example, select this option to stop showing the ad to users who already purchased. 5. In **AD PLATFORM**, choose **Google Ads**. 6. In **Google Ads account**, select the connected ad account. 7. In **Audience**, select the Google Ads audience. [Create one first](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#manage-customer-match-audiences) if none exists yet. ![Audience sync point with Google Ads platform, account, audience, Add or Remove option, Apply and Cancel](https://docs.pushwoosh.com/integrations-google-ads-integration-6.webp) 8. Click **Apply** to save the point or **Cancel** to close without saving. 9. Finish configuring the journey, then launch it. ## Behavior and error handling * Google matches a user only by email, phone, or mobile advertising ID (**MADID**). There is no tag-based fallback. Pushwoosh hashes email and phone before sending them. MADID goes to Google as is. A user with none of these identifiers on their profile is skipped and continues the journey. * Sync to Google runs in the background. The **Audience sync** point accepts the user and moves them to the next step immediately, then applies the add or remove afterward. A delay between reaching the step and the change appearing in Google Ads is expected. * If the selected ad account is disconnected when a profile reaches **Audience sync**, Pushwoosh pauses that journey version and sends system and email notifications. An older connection that is missing the Data Manager permission has the same pause outcome, with a separate audience-sync-failure status in statistics. See [Reconnect if audience uploads fail](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#reconnect-if-audience-uploads-fail). * Google does not use an audience for targeting until it has about 1,000 matched users. A smaller audience keeps accepting syncs and reports success, but Google will not show ads to it until it crosses that threshold. ## Audience sync statistics After launch, check that sync is working: 1. Open statistics for the **Audience sync** step. Check entry volume, adds and removals, and skipped profiles. 2. On the **Google Ads** page in Pushwoosh, open **Customer Match audiences** and check the audience size. Size can stay 0 for up to 48 hours while Google matches users. See [Behavior and error handling](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#behavior-and-error-handling) for when Google starts using the audience for targeting. For metric details, see [**Audience sync**](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#audience-sync) in **Customer Journey statistics**. --- # Google Analytics integration **URL:** https://docs.pushwoosh.com/product/integrations/google-analytics-integration/ Send the same user ID to Google Analytics 4 and Pushwoosh so both tools identify each user the same way across your site and app --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. Send the same user ID to Google Analytics 4 and Pushwoosh, and both tools treat a person who visits your website or app as one user. You can then message the users you analyze in Google Analytics, and build Pushwoosh segments from Google Analytics data. ## Integration overview ### Integration type **Destination:** Google Analytics audiences and the Google Analytics BigQuery export become Pushwoosh segments. The user ID goes from your website or app to each tool separately. Pushwoosh doesn’t send data to Google Analytics. ### Prerequisites * A Pushwoosh account and the Pushwoosh SDK ([Web](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30), [Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/), or [iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/)) on the website or in the app you send the user ID from. * A [Google Analytics 4 property](https://support.google.com/analytics/answer/9304153) that collects data from the same website or app. To import Google Analytics audiences, you also need: * A Google Cloud service account and its JSON key. See [Create and delete service account keys](https://cloud.google.com/iam/docs/keys-create-delete). * The [Google Analytics Data API](https://developers.google.com/analytics/devguides/reporting/data/v1) and [Admin API](https://developers.google.com/analytics/devguides/config/admin/v1) enabled in that service account’s project. * The **Viewer** role for the service account on the Google Analytics property. See [Add, edit, and delete users and user groups](https://support.google.com/analytics/answer/9305788). To query the BigQuery export, you also need: * The Google Analytics 4 property linked to BigQuery. See [Set up BigQuery Export](https://support.google.com/analytics/answer/9823238). * A service account key with the **BigQuery Data Viewer** and **BigQuery Job User** roles. See [BigQuery roles and permissions](https://cloud.google.com/bigquery/docs/access-control). ### How does the integration work? Pushwoosh matches users by their ID: 1. Your website or app sends the same user ID to Google Analytics and to Pushwoosh. 2. Pushwoosh reads user IDs from a Google Analytics audience or from your BigQuery export. 3. A user joins the segment when their ID matches a Pushwoosh User ID. The segment refreshes differently for each source: * A segment from an audience refreshes about once a day while you use it. * A segment from BigQuery reflects activity up to the previous day. ## Setting up the integration Setting up has two parts. First, your developers send the user ID from your website or app. Then you build a segment from Google Analytics data in the Control Panel. 1. With your developers, [choose the user ID](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/#choose-the-user-id). 2. Your developers send it to Google Analytics and to Pushwoosh from your [website](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/#send-the-user-id-from-your-website) or [mobile app](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/#send-the-user-id-from-your-mobile-app). 3. Optionally, [build a Pushwoosh segment](https://docs.pushwoosh.com/product/integrations/google-analytics-integration/#create-a-segment-from-google-analytics-data) from Google Analytics data. You can then use it like any other segment. ### Send the user ID to Google Analytics and Pushwoosh Your developers do this part. Share the sections below with them. #### Choose the user ID Use an identifier your own system already has for each user, such as the account ID from your database. The same value goes to Google Analytics as its user ID and to Pushwoosh as the User ID. Don't use personal information in the user ID [Google Analytics policies](https://support.google.com/analytics/answer/6366371) don’t allow personal information in the user ID. Don’t use an email address, phone number, or other personal information. Set the ID at the moment the user logs in or is otherwise identified. #### Send the user ID from your website Pass the ID to Google Analytics in the `user_id` parameter of your Google tag. If you use Google Tag Manager, set the `user_id` field in your Google tag configuration instead. See [Send user IDs](https://developers.google.com/analytics/devguides/collection/ga4/user-id) in the Google documentation. JavaScript ```javascript gtag('config', 'G-XXXXXXXXXX', { 'user_id': 'USER_ID' }); ``` Pass the same ID to Pushwoosh with the [Web Push SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30): JavaScript ```javascript Pushwoosh.push((api) => { api.registerUser('USER_ID'); }); ``` #### Send the user ID from your mobile app Mobile apps send data to Google Analytics 4 through Firebase. Set the ID with Firebase Analytics `setUserId` (see [Set a user ID](https://firebase.google.com/docs/analytics/userid) in the Firebase documentation), and set the same value with the Pushwoosh SDK. ##### Android Java ```java FirebaseAnalytics.getInstance(context).setUserId("USER_ID"); Pushwoosh.getInstance().setUserId("USER_ID"); ``` ##### iOS Swift ```swift Analytics.setUserID("USER_ID") Pushwoosh.sharedInstance().setUserId("USER_ID") ``` ### Create a segment from Google Analytics data You do this part in the Control Panel, after your developers send the user ID. Pushwoosh can turn Google Analytics data into a segment in two ways, both through [external segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments): * **Import a Google Analytics audience:** pick an audience you already built in Google Analytics. No SQL and no BigQuery needed. * **Query the BigQuery export:** write your own SQL over the raw events Google Analytics exports to BigQuery, for conditions an audience can’t express. #### Import a Google Analytics audience Ask your developer or Google Cloud administrator to do these steps in Google Cloud and Google Analytics: 1. Prepare a service account and its JSON key. See [Create and delete service account keys](https://cloud.google.com/iam/docs/keys-create-delete) in the Google Cloud documentation. 2. In the Google Cloud project of the service account, enable the **Google Analytics Data API** and the **Google Analytics Admin API**. 3. In Google Analytics, go to **Admin** → **Property access management** and add the service account email with the **Viewer** role. A role granted in Google Cloud IAM doesn’t give access to Google Analytics. Then do these steps in the Control Panel: 1. Create an [external segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments) with **Database type** set to **Google Analytics 4 audience**. 2. Enter the **GA4 property ID**. You can find it in Google Analytics under **Admin** → **Property details**. 3. Paste the JSON key from the first list into **Service account JSON**. 4. Click **Load audiences** and choose the **Audience**. ![Google Analytics 4 audience form with GA4 property ID, Service account JSON, the Purchasers audience, and Check connection](https://docs.pushwoosh.com/segmentation-external-segments-8.webp) 5. Click **Check connection**. 6. Click **Create**. Pushwoosh refreshes the segment when it reads the whole segment, for example when you send a campaign or journey to it or calculate its size: * If the data is less than 24 hours old, Pushwoosh uses it as is, because Google Analytics updates audience membership about once a day. * If it’s older than 24 hours, Pushwoosh uses the current data and refreshes the segment in the background. * If it’s older than 72 hours, Pushwoosh waits for the refresh to finish. You can’t refresh the segment more often than once a day. If it hasn’t been refreshed for more than a day, [calculate the segment size](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/calculating-segment-size/) some time before the send. This starts the refresh in the background, so the send uses fresher data. #### Query the BigQuery export If you export your Google Analytics 4 data to BigQuery, you can select users with any SQL condition over their events, for example everyone who made a purchase in the last 30 days. Ask your developer or Google Cloud administrator to do these steps in Google Cloud and Google Analytics: 1. Link your Google Analytics 4 property to a BigQuery project and turn on the daily export. Follow [Set up BigQuery Export](https://support.google.com/analytics/answer/9823238) in the Google documentation. Google Analytics then writes each day of events to a table named `events_YYYYMMDD` in a dataset named `analytics_`. The user ID you sent in the previous sections is in the `user_id` column. 2. Create a service account key with the **BigQuery Data Viewer** and **BigQuery Job User** roles. **Check connection** and **Check query** run a test query job in your project, so they fail without **BigQuery Job User**. Then, in the Control Panel, create an external segment with **Database type** set to **Google BigQuery**. Enter the service account key for the connection and a query that returns the user IDs of the users you want. For example, this query returns users who made a purchase in the last 30 days. Replace `my-project` with your Google Cloud project ID and `analytics_123456789` with your dataset name: SQL ```sql SELECT DISTINCT user_id FROM `my-project.analytics_123456789.events_*` WHERE event_name = 'purchase' AND user_id IS NOT NULL AND _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY)) ``` The segment reflects activity up to the previous day, because Google Analytics exports data once a day. ## How do you know that the integration is working? * **User ID:** in Pushwoosh, go to **Audience** → **User Explorer** and [search by User ID](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#search-for-a-user) for an ID you sent to Google Analytics. If the user is found, both tools have the same ID. * **Segments:** on **Audience** → **External Segments**, the segment’s status is **valid**. To see how many users matched, [check the segment size](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/#check-how-many-people-are-in-the-segment). ## Troubleshooting ### Load audiences or Check connection shows an error Both buttons sign in with the service account key and read the property’s audience list, and the form shows the error Google Analytics returns. Check that: * **Service account JSON** is a service account key. Other Google credential files are rejected. * **GA4 property ID** is the number from **Admin** → **Property details**. * The service account has the **Viewer** role in Google Analytics **Admin** → **Property access management**. A role granted in Google Cloud IAM doesn’t count. * The Google Analytics Admin API is enabled in the service account’s Google Cloud project. ### The audience list is empty The property has no audiences yet. Create one in Google Analytics under **Admin** → **Audiences**, then click **Load audiences** again. ### Nobody gets into the segment * Make sure the Google Analytics Data API is enabled. **Check connection** doesn’t test it, but Pushwoosh needs it to read audience members. * Make sure your website or app sends the same user ID to Google Analytics and to Pushwoosh. Only users whose Google Analytics user ID matches a Pushwoosh User ID get into the segment. Users Google Analytics knows only by its anonymous client ID don’t get in. ### Check connection or Check query fails for BigQuery Both checks run a test query job in your Google Cloud project without reading any data. Check that: * The service account has both the **BigQuery Data Viewer** and **BigQuery Job User** roles. Without **BigQuery Job User**, neither check can run. * The query uses your project ID and the `analytics_` dataset name, and the export has already written at least one daily `events_YYYYMMDD` table. * The query returns exactly one column, named `user_id`. --- # Google BigQuery integration **URL:** https://docs.pushwoosh.com/product/integrations/google-bigquery-integration/ Stream Pushwoosh message events to Google BigQuery for near real-time analytics, reporting, and downstream data workflows. --- Developer assistance needed You’ll need help from your development team or Google Cloud administrator to set up the integration. Please share this guide with them. The [Google BigQuery](https://cloud.google.com/bigquery) integration streams selected Pushwoosh message events to your BigQuery dataset. Use it to analyze push, email, and SMS lifecycle events in BigQuery, build custom reports, or connect the data to your downstream analytics workflows. This integration only sends data one way, from Pushwoosh to BigQuery. To import a segment of BigQuery users into Pushwoosh instead, see [External segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments#bigquery). ## Integration overview ### Prerequisites Prepare the following before you open the integration setup. 1. Use a Google Cloud project with billing enabled. Free Trial credits are supported. BigQuery Sandbox is not enough because the Storage Write API requires billing. 2. Make sure you have a paid Pushwoosh account. ### Pricing You pay Google directly for BigQuery usage. Pushwoosh does not charge for the integration itself. For current rates, free tiers, and regional details, see [BigQuery pricing](https://cloud.google.com/bigquery/pricing). Costs can include: * **Data ingestion:** Pushwoosh streams events with the BigQuery Storage Write API. * **Storage:** BigQuery stores the rows written to your destination table. * **Queries:** BigQuery charges for queries based on your selected pricing model. ### Integration type **Source:** Data is sent from Pushwoosh to your BigQuery dataset. ### Supported platforms Pushwoosh streams events from iOS, Android, Huawei, Chrome, Safari, Firefox, and Web platforms. ### Synchronized entities Selected push, email, and SMS lifecycle events are streamed to BigQuery. Pushwoosh writes one row per selected event to the destination table. ### Use cases * **Near real-time message analytics:** analyze push, email, and SMS lifecycle events in BigQuery shortly after they are processed in Pushwoosh. * **Custom reporting:** build BigQuery reports for selected event kinds, applications, campaigns, and message identifiers. * **Downstream data workflows:** connect Pushwoosh event data to your analytics, reporting, or data processing workflows. ### How the integration works After you save the configuration, Pushwoosh begins streaming selected message events to your BigQuery table in near real time. For every message event that flows through Pushwoosh, the system checks whether the event kind is selected in your configuration. If it is, Pushwoosh appends a new row to your destination table. If the table does not exist yet, Pushwoosh creates it automatically using the schema described below. Events typically appear in BigQuery within 30 seconds of being processed in Pushwoosh. ## Set up the integration in Google Cloud ### Choose a Google Cloud project Sign in to [Google Cloud Console](https://console.cloud.google.com), then pick or create the project that will own the BigQuery dataset. ![Google Cloud Console project selector showing the selected project and project ID for BigQuery setup](https://docs.pushwoosh.com/integrations-google-bigquery-integration-4.webp) Tip Note the **Project ID**. Use the short identifier, for example `my-company-12345`, not the human-readable project name. ### Enable the required APIs In Google Cloud Console, go to **APIs & Services** → **Library** and enable these APIs: * **BigQuery API** * **BigQuery Storage API** ![Google Cloud APIs Library showing BigQuery API and BigQuery Storage API enabled for the project](https://docs.pushwoosh.com/integrations-google-bigquery-integration-5.webp) Pushwoosh uses these APIs to create the destination table and stream events into BigQuery. ### Create a Service Account Pushwoosh uses the Service Account to write events to your BigQuery dataset. 1. Go to **IAM & Admin** → **Service Accounts**. 2. Click **Create service account**. 3. In **Service account name**, enter a name, for example, `pushwoosh-bigquery`. Google Cloud automatically generates the **Service account ID** from the name. ![Google Cloud Create service account screen with service account name and generated service account ID](https://docs.pushwoosh.com/integrations-google-bigquery-integration-6.webp) 4. Click **Create and continue**. ### Grant IAM roles 1. Grant the Service Account these IAM roles: * **BigQuery Data Editor:** lets Pushwoosh create the table and append rows. * **BigQuery User:** lets Pushwoosh use the Storage Write API. ![Google Cloud IAM role selection screen showing BigQuery Data Editor and BigQuery User roles](https://docs.pushwoosh.com/integrations-google-bigquery-integration-7.webp) Note You can attach the roles at the project level or at the dataset level. Project-level access is the easiest to set up. Dataset-level access is more restrictive and recommended for production. 2. Click **Continue**. 3. Click **Done**. ### Create a JSON key Pushwoosh uses the JSON key to authenticate as the service account. 1. Open the service account you created. 2. Go to **Keys** → **Add key** → **Create new key**. ![Google Cloud Service Account Keys tab with Add key and Create new key menu options](https://docs.pushwoosh.com/integrations-google-bigquery-integration-8.webp) 3. Select **JSON**. Google Cloud downloads the JSON key file to your computer. ![Google Cloud Create private key dialog with JSON key type selected for the Service Account](https://docs.pushwoosh.com/integrations-google-bigquery-integration-11.webp) Caution Keep the JSON key file safe. Anyone with this file can write to your BigQuery project. Do not commit it to git or share it over chat. Pushwoosh stores the key encrypted at rest and does not expose it through the UI after upload. ### Create a dataset The dataset is where Pushwoosh stores the streamed event table. 1. In Google Cloud Console, open **BigQuery**. 2. In **Explorer**, select the project you prepared for the integration. 3. Click **Create dataset**. ![BigQuery page in Google Cloud Console with Create dataset action for choosing a dataset location](https://docs.pushwoosh.com/integrations-google-bigquery-integration-9.webp) 4. In **Dataset ID**, enter a dataset ID, for example `pushwoosh_data`. 5. In **Data location**, select the dataset region. ![Create dataset form in BigQuery showing Dataset ID and location fields for the dataset region](https://docs.pushwoosh.com/integrations-google-bigquery-integration-10.webp) 6. Click **Create dataset**. ## Configure the integration in Pushwoosh 1. In your Pushwoosh account, go to **Settings** → **3rd Party Integrations** for the application you want to connect. 2. Find **Google BigQuery** in the list of available services and click **Configure**. ![3rd Party Integrations page with the Google BigQuery card available for configuration](https://docs.pushwoosh.com/integrations-google-bigquery-integration-1.webp) 3. Fill in the configuration fields. * **GCP Project ID:** enter the project ID from Google Cloud, for example `my-company-12345`. * **Service Account JSON:** paste the full contents of the JSON key file you downloaded from Google Cloud. * **Dataset ID:** once **GCP Project ID** and **Service Account JSON** are filled, Pushwoosh fetches the datasets your Service Account can access. Select the destination dataset. If the dropdown is empty, check that the Service Account has access and that the dataset exists in the project you specified. * **Dataset region:** select the region of your BigQuery dataset. * **Table name:** leave blank to use the default `pushwoosh_events` table. Pushwoosh creates the table with the schema described below. * **Events:** select the events you want to stream. You can change this list later. * **Stream events to BigQuery:** enable this toggle. Turn it off to pause streaming without deleting the configuration. 4. Click **Test connection**. Pushwoosh validates the credentials against BigQuery without writing data. You may see one of these connection statuses: * **Connection successful:** the credentials work and the Service Account can access the dataset. * `auth_failed`: the JSON key is invalid or revoked. * `dataset_not_found`: the dataset ID is incorrect or the Service Account cannot access it. * `missing_permission`: the Service Account is missing one of the required roles. 5. Click **Apply**. Pushwoosh saves the configuration and starts using it within about 30 seconds. After that, selected events start streaming to BigQuery. ## Verify the integration 1. Send a test push, or trigger another message that produces one of the event kinds you selected. 2. Wait about 30 seconds. 3. Open [BigQuery Studio](https://console.cloud.google.com/bigquery). 4. Go to your project, then open the dataset and the destination table you configured. If you left **Table name** blank, open `pushwoosh_events`. 5. Click **Preview**. You should see the event row in the table. ![BigQuery Studio table preview showing Pushwoosh event rows in the pushwoosh_events table](https://docs.pushwoosh.com/integrations-google-bigquery-integration-3.webp) ### Table schema Pushwoosh writes each selected event as a separate row in the destination table. To make queries faster and easier to filter, the table is partitioned by day using `timestamp` and clustered by `app_id` and `event_kind`. | Field name | Type | Description | | :----------- | :---------- | :------------------------------------------------------------------------- | | `event_kind` | `STRING` | Pushwoosh event kind, for example `Push Sent` or `Email Opened`. | | `message_id` | `STRING` | Pushwoosh message code, such as the campaign or message identifier. | | `device_id` | `STRING` | Pushwoosh hardware ID of the device that produced the event. | | `user_id` | `STRING` | Your external user ID if known. Empty for anonymous devices. | | `timestamp` | `TIMESTAMP` | Event time in UTC. | | `app_id` | `STRING` | Pushwoosh application code. | | `platform` | `STRING` | Source platform, for example `ios`, `android`, or `web`. | | `properties` | `JSON` | Additional event fields. Use `JSON_VALUE` to query fields, as shown below. | ### Query properties The `properties` column stores additional event fields as JSON. Use `JSON_VALUE` to extract individual fields in your queries. For example, to see which campaigns drove the most opens over the last 7 days, click **+** to create a new query, paste the SQL below, and click **Run**. ```sql SELECT event_kind, JSON_VALUE(properties, '$.campaign_id') AS campaign_id, COUNT(*) AS events FROM `your-project.your_dataset.pushwoosh_events` WHERE event_kind = 'Push Opened' AND timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY) GROUP BY 1, 2 ORDER BY events DESC ``` To review event counts for the last hour, run this query: ```sql SELECT event_kind, COUNT(*) AS events FROM `your-project.your_dataset.pushwoosh_events` WHERE timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 1 HOUR) GROUP BY event_kind ORDER BY events DESC ``` ## Update the integration ### Rotate the Service Account key 1. In Google Cloud Console, go to **IAM & Admin** → **Service Accounts**. 2. Open your Service Account. 3. Go to **Keys** and create a new JSON key. 4. Keep the old key active until you confirm the new key works. 5. In Pushwoosh, open the **Google BigQuery** configuration modal. 6. Paste the new JSON into **Service Account JSON**. 7. Click **Apply**. Pushwoosh validates the new key, replaces the stored credential, and starts using it after the next configuration reload, which takes about 30 seconds. After you confirm that events are still flowing, delete the old key in Google Cloud Console. ### Change the destination dataset or table 1. In Pushwoosh, go to **Settings** → **3rd Party Integrations**. 2. Open **Google BigQuery** settings. 3. Select a different dataset or enter a new table name. 4. Click **Apply**. Pushwoosh reopens the stream with the new destination within about 30 seconds. Rows already written stay in the old table. Pushwoosh does not backfill historical data. To keep the stored Service Account key unchanged when you update other settings, leave **Service Account JSON** empty before you click **Apply**. ## Troubleshooting | Issue | What to check | | :---------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Test connection fails with `auth_failed`** | The Service Account JSON is malformed or the key has been revoked in Google Cloud. Create a new key and paste the full JSON file again. The file starts with `{`, ends with `}`, and contains a `private_key` block. | | **Test connection fails with `dataset_not_found`** | The **Dataset ID** is misspelled or does not exist in the project you specified. Dataset IDs are case-sensitive. Select the dataset from the dropdown to avoid typos. | | **Test connection fails with `missing_permission`** | The Service Account is missing **BigQuery Data Editor** or **BigQuery User**. Grant both roles at the project level, or grant them at the dataset level for more restrictive access. | | **Test connection passes, but no rows appear in BigQuery** | Wait at least 30 seconds. Check that the event kind you are sending is selected in **Events**. For example, if only **Push Opened** is selected and nobody opens the push, no rows appear. | | **Configuration looks correct, but the modal shows empty fields** | Reload the page. The configuration is fetched on each modal open and cached for 30 seconds by the underlying service. If you just saved the settings, wait a moment and open the modal again. | Note Pushwoosh records **Push Sent**, **Email Sent**, and **SMS Sent** regardless of delivery status so BigQuery aggregates match canonical Pushwoosh statistics. For **Delivered**, **Opened**, **Bounced**, and **Unsubscribed**, Pushwoosh records only successful events. ## FAQ ##### Can I use a free Google Cloud account? Yes, as long as billing is enabled on the project. The Free Trial credits are enough to run this integration at typical volumes for the full trial period. BigQuery Sandbox without billing will not work because the Storage Write API requires billing. ##### Does Pushwoosh see my BigQuery data? No. The Service Account credential you upload authorizes Pushwoosh to write to the dataset you select. Pushwoosh does not read from your dataset and has no access to the rest of your project. ##### Can I export to multiple BigQuery datasets? One destination is supported per application. If you need the same events in two datasets, set up a BigQuery scheduled query in your project to copy data from `pushwoosh_events` into another table. ##### Can I change the table schema? The schema is fixed across all customers. If you need additional columns, extract them from the `properties` JSON in your own views or scheduled queries. ##### What happens if I disable the integration temporarily? Turn off **Stream events to BigQuery** and click **Apply**. Pushwoosh stops appending events for this application within about 30 seconds. Events produced while the integration is off are not buffered or backfilled when you turn it back on. Pushwoosh keeps the configuration, including credentials, dataset, and event selection. ##### How do I delete the integration completely? Contact to delete the integration configuration. The dataset and rows already written to BigQuery remain in your Google Cloud account. ##### Are there delivery guarantees? The integration uses at-least-once delivery. Under normal operation, duplicates are rare. A process restart between an append and a commit can produce a small number of duplicate rows. Deduplicate in SQL if your downstream pipeline requires exactly-once results. ##### Why is there no Push Clicked event? Pushwoosh currently exposes **Push Sent**, **Push Delivered**, and **Push Opened** for push notifications in this integration. A dedicated push click step is not available. Email and SMS have their lifecycle events. --- # Google Play subscription tracking **URL:** https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/ Connect Google Play RTDN to Pushwoosh and track purchases, renewals, cancellations, billing issues, expirations, and refunds. --- Developer assistance needed You’ll need help from your development team to set up this integration. Please share this guide with them. ## Integration overview [Real-Time Developer Notifications (RTDN)](https://developer.android.com/google/play/billing/rtdn-reference) is Google Play’s server-to-server service that sends a real-time message whenever a subscription’s status changes. By connecting Google Play RTDN to Pushwoosh, you can react to the entire subscription lifecycle, including purchases, renewals, cancellations, billing problems, expirations, and refunds — without building your own backend infrastructure. Whenever a subscription’s status changes in a user’s Google Play account, Google notifies Pushwoosh, and Pushwoosh fires the matching [`PW_Subscription*`](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/#tracked-events) event on the user profile. Note This integration supports **Android subscriptions** (Google Play RTDN). To track iOS subscriptions, see [App Store subscription tracking](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/). ### Integration type **Source:** Real-Time Developer Notifications are sent from Google Play to Pushwoosh. ### Tracked events Pushwoosh maps every supported Google Play notification to a unified `PW_Subscription*` event set, so you can trigger campaigns on any stage of the subscription lifecycle. | Event | Fires when | | ----------------------------- | ------------------------------------------------------------------------------ | | `PW_SubscriptionStart` | A user buys the subscription for the first time. | | `PW_SubscriptionRenew` | The subscription auto-renews for a new billing period. | | `PW_SubscriptionCancel` | A user turns off auto-renewal. The subscription stays active until it expires. | | `PW_SubscriptionResume` | A user restarts the subscription before it lapses. | | `PW_SubscriptionBillingIssue` | A renewal payment fails and the subscription enters its grace period. | | `PW_SubscriptionRecovered` | A previously failed renewal goes through and the subscription is active again. | | `PW_SubscriptionExpired` | The subscription has fully lapsed and is no longer active. | | `PW_SubscriptionRefund` | Google Play revokes the subscription (for example, after a refund). | Every event carries the same attributes: * **productID:** the Google Play product identifier of the subscription. * **expiresAt:** when the current paid period ends, as a Unix timestamp in seconds. Included when Google provides it. How events map to Real-Time Developer Notifications For developers verifying the integration, each Pushwoosh event corresponds to these RTDN `notificationType` values: | Pushwoosh event | RTDN `notificationType` | | ----------------------------- | ---------------------------------- | | `PW_SubscriptionStart` | `SUBSCRIPTION_PURCHASED` (4) | | `PW_SubscriptionRenew` | `SUBSCRIPTION_RENEWED` (2) | | `PW_SubscriptionCancel` | `SUBSCRIPTION_CANCELED` (3) | | `PW_SubscriptionResume` | `SUBSCRIPTION_RESTARTED` (7) | | `PW_SubscriptionBillingIssue` | `SUBSCRIPTION_IN_GRACE_PERIOD` (6) | | `PW_SubscriptionRecovered` | `SUBSCRIPTION_RECOVERED` (1) | | `PW_SubscriptionExpired` | `SUBSCRIPTION_EXPIRED` (13) | | `PW_SubscriptionRefund` | `SUBSCRIPTION_REVOKED` (12) | Other notification types, such as on-hold, price changes, deferrals, and pauses, are acknowledged but don’t post an event. ### How it works A Google Play notification carries no Pushwoosh identifier. It includes only a purchase token and the app’s `packageName`. So your app tags each purchase with the identifier Pushwoosh needs, and Pushwoosh reads it back from the purchase whenever a notification arrives. 1. A subscription’s status changes in a user’s Google Play account (a purchase, renewal, cancellation, and so on). 2. Google Play publishes an RTDN message to Pushwoosh’s shared topic. 3. Pushwoosh reads the purchase’s `obfuscatedAccountId`, which your app set to `:` at purchase time. 4. Pushwoosh resolves the device whose HWID matches, finds the user bound to it, and posts the matching `PW_Subscription*` event for that user. Important The match between a Google Play purchase and a Pushwoosh user relies on the `obfuscatedAccountId`. If your app doesn’t set this value at purchase time, Pushwoosh receives the notification but **no event is posted**. It is set at purchase and **cannot be backfilled** for existing subscriptions. See [how to set the account identifier](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/#set-the-account-identifier-at-purchase). ### Use cases **Win back churning subscribers:** Disabling auto-renewal doesn’t end access right away. The subscription stays active until the paid period ends, and that’s your window to win the user back. On `PW_SubscriptionCancel`, launch a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) with a retention push, an [email](https://docs.pushwoosh.com/product/messaging-channels/emails/) about features they’d lose, or an [in-app message](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) with a renewal discount before access lapses. **Onboard new subscribers:** Trigger a welcome series on `PW_SubscriptionStart` to help users get value from their plan early and set the stage for renewal. **Rescue failed payments:** When `PW_SubscriptionBillingIssue` fires, a renewal payment didn’t go through and the subscription is in its grace period. Prompt the user to update their payment method before they lose access, and follow up with `PW_SubscriptionRecovered` to confirm once it’s resolved. **Re-engage lapsed users:** Start a reactivation campaign on `PW_SubscriptionExpired` with a returning-customer offer for subscribers who have fully churned. ## Setting up the integration Before you start, make sure you have a Pushwoosh app with [FCM configured](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/firebase-integration/integrate-pushwoosh-android-sdk/) (already required for push), a Google Play app with a subscription, and Play Console admin access. ### Set the account identifier at purchase Pushwoosh identifies the right user from the device’s **HWID**, combined with your **Application Code**. The Pushwoosh Android SDK exposes a helper, `getSubscriptionAccountId()`, that returns this value already formatted as `:`. Pass it to `BillingFlowParams.setObfuscatedAccountId()` when you launch the Google Play billing flow. * Kotlin ```kotlin val billingParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) // Tag the purchase with the Pushwoosh account identifier ":" .setObfuscatedAccountId(Pushwoosh.getInstance().subscriptionAccountId) .build() billingClient.launchBillingFlow(activity, billingParams) ``` * Java ```java BillingFlowParams billingParams = BillingFlowParams.newBuilder() .setProductDetailsParamsList(productDetailsParamsList) // Tag the purchase with the Pushwoosh account identifier ":" .setObfuscatedAccountId(Pushwoosh.getInstance().getSubscriptionAccountId()) .build(); billingClient.launchBillingFlow(activity, billingParams); ``` Note Call `getSubscriptionAccountId()` after the SDK is initialized. It returns an empty string if the Application Code or HWID isn’t available yet. Google limits the obfuscated account id to 64 characters. The Pushwoosh `:` value stays within that limit. Caution If your app overrides the Pushwoosh HWID with a custom value, `getSubscriptionAccountId()` reflects it automatically. Don’t build the identifier by hand. Always use the helper so the value matches the device’s HWID in Pushwoosh, or the event cannot be attributed. ### Point Real-Time Developer Notifications at Pushwoosh 1. In [Google Play Console](https://play.google.com/console/), go to **Monetize → Monetization setup**. 2. Find **Real-time developer notifications** and set the **Topic name** to: ```plaintext projects/pw-playstore-subscriptions/topics/play-rtdn ``` 3. Click **Save**. Publish permission is already granted to Google’s notification service, so there’s nothing else to configure here. ### Grant Pushwoosh’s service account 1. In Google Play Console, go to **Users and permissions → Invite new user**. 2. Enter the Pushwoosh service account email: ```plaintext play-api@pw-playstore-subscriptions.iam.gserviceaccount.com ``` 3. Under **App permissions**, add your app and grant **View financial data, orders, and cancellation survey responses** (plus the read-only app information permission). 4. Click **Save**. A service account doesn’t need to accept the invite. Access is active immediately. ### Confirm the events in Pushwoosh Pushwoosh registers each `PW_Subscription*` event in your project the first time it occurs, with the `productID` and `expiresAt` attributes. After a test, open **Audience → Events** to verify the events appear. They are then ready for segmentation, statistics, and Customer Journeys. ### Build your campaign Create a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) with a [trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on any `PW_Subscription*` event, for example `PW_SubscriptionCancel` for win-back or `PW_SubscriptionStart` for onboarding, and add the messages you want to send. ## Testing To verify the integration end to end: 1. In Google Play Console, open **Monetization setup** and click **Send test notification**. It should report success, confirming the topic is wired correctly. 2. Make a subscription purchase with the account identifier set as described above (this fires `PW_SubscriptionStart`), then cancel it from **Play Store → Subscriptions → Cancel** (this fires `PW_SubscriptionCancel`). 3. In Pushwoosh Control Panel, open the user profile and go to [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab). 4. Confirm that the events appear within a few moments. --- # HubSpot integration **URL:** https://docs.pushwoosh.com/product/integrations/hubspot-integration/ Connect Pushwoosh with HubSpot to sync contacts, send targeted push notifications, and automate campaigns via workflows. --- [**HubSpot**](https://www.hubspot.com/) is a leading CRM platform used for marketing, sales, and customer service automation. By integrating Pushwoosh with HubSpot, you can sync contact data between both systems, trigger push notifications based on CRM events, and enhance customer engagement through automated, personalized messaging. ## Integration overview ### Integration type * **Source:** Pushwoosh writes back user events or segmentation tags into HubSpot. * **Destination:** HubSpot sends contact or workflow data to Pushwoosh. ### Prerequisites * A Pushwoosh account with a valid [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). * A HubSpot account with admin access to install integrations. ### Integration scope This integration is configured per Pushwoosh account. Once installed, it becomes available across all projects within the account. ### Glossary (mapping entity names if they are different) | Pushwoosh | HubSpot equivalent | | :-------- | :----------------- | | Event | Trigger | ### Synchronized entities * **Contacts:** Pushwoosh and HubSpot contacts are synced in both directions. * **Events:** Pushwoosh push statistics are linked to corresponding HubSpot contact records. ### Use cases * Send personalized push notifications during HubSpot workflows. * Trigger automated push messages based on contact behavior or data using workflows. ## Set up the integration ### Add Pushwoosh integration to HubSpot To integrate Pushwoosh with your HubSpot account, follow these steps: 1. Log in to your HubSpot account. 2. Click the installation button below to install the application. [Install App](https://app-eu1.hubspot.com/oauth/authorize?client_id=4ebbb2ac-46a4-48a0-af4a-21588aba2e26&redirect_uri=https://integration-hubspot.svc-nue.pushwoosh.com/oauth-callback/v3&scope=crm.objects.contacts.write%20crm.objects.contacts.read%20timeline) 3. In the installation window, select the HubSpot account you want to connect. 4. Click **Choose Account** to proceed. ![Add Pushwoosh integration to HubSpot](https://docs.pushwoosh.com/integrations-hubspot-integration-1.webp) 5. Review the required access, then click **Connect App**. ![Required access in HubSpot integration](https://docs.pushwoosh.com/integrations-hubspot-integration-2.webp) 6. After a successful install, in HubSpot go to **Integrations**, then **Connected Apps**. ![HubSpot Connected Apps on Connections home showing the My Apps list with Pushwoosh](https://docs.pushwoosh.com/integrations-hubspot-integration-18.webp) 7. In the **My Apps** section, find **Pushwoosh**. On the right side of the row, click **Actions**, then select **Go to settings**. ![Pushwoosh row in My Apps with Actions menu open and Go to settings option visible](https://docs.pushwoosh.com/integrations-hubspot-integration-21.webp) 8. Open the **Settings** tab. In the form that appears, enter your Pushwoosh [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token), then click **Connect**. ![Add Server API token in Pushwoosh HubSpot integration settings](https://docs.pushwoosh.com/integrations-hubspot-integration-3.webp) 9. After a successful connection, your Pushwoosh account name appears in the **Settings** tab. You can disconnect applications at any time. ![HubSpot Pushwoosh integration Settings tab with Connected to Pushwoosh, account, and Disconnect](https://docs.pushwoosh.com/integrations-hubspot-integration-22.webp) Note If you remove the Pushwoosh connection from HubSpot by selecting **Disconnect** on the Pushwoosh **Settings** tab, you must set up the integration again before you can use it. Repeat steps 2 through 9 in this section. When you finish these steps, you can continue in HubSpot and use Pushwoosh features there. ### Set up a CRM card in HubSpot contacts You can display Pushwoosh data directly on the HubSpot contact page by adding the CRM card. To do this: 1. Go to **Contacts**. Open any contact from your contacts list. 2. Click the **Customize** button in the top-right corner of the middle section. ![Set up the Pushwoosh CRM card](https://docs.pushwoosh.com/integrations-hubspot-integration-5.webp) 3. On the page that opens, select the view where you want to add the Pushwoosh extension, then click its name. By default, this is usually **the Default view**. ![View list in HubSpot contacts](https://docs.pushwoosh.com/integrations-hubspot-integration-6.webp) 4. In the opened editor, select the required tab or location, then choose **Add new card**. In the menu that appears, go to the **Card library** page and enter **Pushwoosh** in the **Search cards** field. You can now add the Pushwoosh extension to your view. ![Card library for adding Pushwoosh extension](https://docs.pushwoosh.com/integrations-hubspot-integration-7.webp) 5. Close the Card Library and click **Save and exit** to save your changes. The Pushwoosh extension is now available in your contact view and ready to use. ## Use the Pushwoosh extension The Pushwoosh extension allows you to send notifications directly to any contact, either from the HubSpot contact page or as part of a HubSpot workflow. ### Send a notification To send a notification using the Pushwoosh extension, follow these steps: 1. **Select a Pushwoosh application**. If you have multiple Pushwoosh applications, choose the one you want to send the notification from. Note An application is required, and you must select one from the list. ![Select a Pushwoosh application](https://docs.pushwoosh.com/integrations-hubspot-integration-16.webp) 2. **Select a notification type (platform)** from the dropdown, which lists all available options for the selected Pushwoosh application. ![Notification types (platforms) dropdown](https://docs.pushwoosh.com/integrations-hubspot-integration-9.webp) 3. You can select a notification preset or manually enter the **Title** and **Message**.\ Presets contain saved content with pre-filled fields. ![Notification data with title and message fields](https://docs.pushwoosh.com/integrations-hubspot-integration-10.webp) 4. Send the notification. Note The **Identifier (HWID / User ID)** field contains the unique identifier of the user who will receive the notification. ## Send notifications in HubSpot workflows Important This feature is available only for contact-based workflows. Pushwoosh allows you to send notifications directly from HubSpot workflows. To add a Pushwoosh notification to your workflow: 1. Open your HubSpot workflow and add an action. 2. In the **Choose an action** side panel, scroll to **Integrated apps**. 3. Expand **Pushwoosh** and select **Send Notification with Pushwoosh**. ![Pushwoosh application in HubSpot workflows action](https://docs.pushwoosh.com/integrations-hubspot-integration-19.webp) 4. Fill in the fields the same way as in the Pushwoosh extension, then click **Save**. ![Notification action settings in HubSpot](https://docs.pushwoosh.com/integrations-hubspot-integration-20.webp) 5. Save the workflow. ### Add Pushwoosh triggers to a HubSpot workflow You can also add triggers from the Pushwoosh application to your HubSpot workflow. To do this: 1. In the workflow editor, under **Trigger enrollment**, click **Choose condition to filter records**. ![Workflow triggers card in HubSpot](https://docs.pushwoosh.com/integrations-hubspot-integration-12.webp) 2. In the side panel that opens, click **Add trigger.** ![Workflow trigger selection panel](https://docs.pushwoosh.com/integrations-hubspot-integration-13.webp) 3. Search for and select **Push Statistics Event** in **Triggers**. ![Push Statistics Event trigger option](https://docs.pushwoosh.com/integrations-hubspot-integration-14.webp) 4. Click **Add criteria** to define the trigger conditions.\ You can select and configure any of the available options from the list of criteria. ![Add criteria button for trigger conditions](https://docs.pushwoosh.com/integrations-hubspot-integration-17.webp) 5. Save the workflow. ## Pushwoosh app events in HubSpot Pushwoosh automatically tracks notification delivery events and syncs them to HubSpot as app events. When you send a notification through Pushwoosh (**Email**, **SMS**, **WhatsApp**, or **Push**), Pushwoosh looks up the related HubSpot contact by email, phone number, or the custom contact property `userid`. If Pushwoosh finds a matching contact, it creates an app event on the contact record with delivery details, including event type (for example, `Email Sent` or `SMS Delivered`), platform, user ID, and device ID. This gives your team direct visibility into Pushwoosh communication history in each HubSpot contact record. This feature works automatically. To turn event visibility on: 1. Go to **Contacts** and open a contact record. 2. Open the **Activities** tab on the contact record. 3. Open **Filter activity**. 4. In the **Integrations** column, select **Pushwoosh**. ![HubSpot contact Activities filters with Pushwoosh selected to display Pushwoosh delivery status app events](https://docs.pushwoosh.com/integrations-hubspot-integration-23.webp) You can now see Pushwoosh delivery events in the contact activity feed. --- # Inbound webhook integration **URL:** https://docs.pushwoosh.com/product/integrations/inbound-webhook/ Use inbound webhooks to receive third-party events in Pushwoosh and trigger customer journeys without a custom integration server. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. Inbound webhooks let third-party services send events directly to Pushwoosh. When a third-party service triggers a webhook, Pushwoosh authenticates the request, identifies the user, and applies the mapping you configured: fill in tags on the user profile, fire a Pushwoosh event, or both. A fired event can then start or advance a journey. Use inbound webhooks to connect tools like CRMs, e-commerce platforms, or analytics services without building or maintaining your own server. If the incoming identifier doesn’t match an existing user, Pushwoosh can create the user instead of dropping the request. Turn on the auto-create checkbox described in [Map incoming data](https://docs.pushwoosh.com/product/integrations/inbound-webhook/#map-incoming-data). ## Before you start Prepare the following before you open the webhook setup. 1. **Decide what the webhook should do.** A webhook must map at least one tag, one event, or both. To fire an event, pick an existing one from your project (for example, `CheckoutSuccess`) or [create one](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) with the attributes you want to fill from the incoming data. Tags can be mapped to existing tags or created on the spot while setting up the webhook. 2. **Make sure your third-party service can send webhooks.** The service must be able to send an HTTP POST request to an external URL when the event you care about happens, for example a new order or a form submission. 3. **Get a sample JSON payload from your third-party service.** This is a small example of the data the service sends on each event. You will need it to map payload fields to tags and event attributes. ## Create a webhook ### Open the webhook setup 1. In your Pushwoosh account, go to **Settings** → **Integrations** → **Inbound webhooks** and click **Settings**. ![Integrations page with Inbound webhooks card and Settings button to open webhook configuration](https://docs.pushwoosh.com/integrations-inbound-webhook-1.webp) 2. Click **Create webhook** to open the setup screen: **Paste a sample payload** on the left, **Webhook setup** on the right. ![Empty Create webhook screen with the payload editor on the left and the blank Webhook setup panel on the right](https://docs.pushwoosh.com/integrations-inbound-webhook-16.webp) 3. Enter a webhook name so you can identify the webhook in the list later. ### Map incoming data 1. In **Paste a sample payload**, paste a sample JSON payload from your third-party service. Pushwoosh extracts the fields and loads them into the payload-field dropdowns. Note If you update the payload later, click **Reload select options** to refresh the dropdowns. This resets your current field mapping. You will need to remap your fields. Sample payload: ```json { "id": "12345", "email": "jane@example.com", "phone": "+15551234567", "loyalty_tier": "gold", "order_number": "ORD-001", "price": 99.99 } ``` 2. In **Identify users by**, choose how Pushwoosh should match the incoming request to a user: * **User ID:** your internal identifier assigned to a user in the system. * **Email:** matches by email address. * **Phone:** matches by phone number. * **HWID:** a device, browser, or email identifier. * **Token:** matches by a device push token. 3. In **Payload field**, select the field that contains the matching value. ![Identify users by dropdown, Payload field selector, and Create a new User ID if no match is found checkbox](https://docs.pushwoosh.com/integrations-inbound-webhook-13.webp) 4. Optional: turn on the checkbox below **Payload field** to create a new user automatically when no match is found, instead of dropping the request. Its label matches your identifier choice, for example **Create a new User ID if no match is found** or **Create a new Email if no match is found**. The checkbox is disabled for HWID and Token, because Pushwoosh can’t create a device, browser, or push-token identifier ahead of an actual SDK session. Each webhook must map at least one tag, fire one event, or both. #### Add tags to profile Use **Add tags to profile** to save payload values as tags on the matched user profile. Fill in profile data like plan tier or city for better segmentation. 1. Click **+ Add tag**. 2. In **Tag name**, pick an existing tag from the list, or type a new name. Pushwoosh shows **Create:** `` to confirm it will create one. 3. If you picked an existing tag, **Type** shows its type and can’t be changed. If you created a new tag, open **Type** and choose its data type. Pushwoosh saves it as a user-specific tag of this type. 4. In **Payload field**, select the field from your sample payload that holds the value. 5. Repeat steps 1–4 for every tag you want to fill. To remove a row, click **×**. ![Add tags to profile section with Tag name, Type, and Payload field row mapping loyalty_tier to a Loyalty tier string tag](https://docs.pushwoosh.com/integrations-inbound-webhook-14.webp) #### Record an event Use **Record an event** to fire a Pushwoosh event when the webhook receives a valid request. A fired event can start or advance a journey. 1. In **Event**, select the Pushwoosh event to fire. 2. Click **+ Add attribute**. 3. In **Event attribute**, pick an existing attribute of the selected event from the list, or type a new name. Pushwoosh shows **Create:** `` to confirm it will create one. 4. If you picked an existing attribute, **Type** shows its type and can’t be changed. If you created a new attribute, open **Type** and choose its data type. 5. In **Payload field**, select the field from your sample payload that holds the value. 6. Repeat steps 2–5 for every attribute you want to fill. To remove a row, click **×**. ![Record an event section with CheckoutSuccess event selected and an event attribute row mapping total (Price type) to price](https://docs.pushwoosh.com/integrations-inbound-webhook-15.webp) ### Enable and connect 1. When configuration is complete, click **Enable webhook**. The **Webhook URL** window opens. Developer assistance needed The next steps require access to your third-party service’s webhook settings. Share the URL and Secret with your development team. 2. Copy the **URL** and set it as the webhook destination in your third-party service. 3. Copy the **Secret** and paste it into your third-party service as the `Authorization` header value. The value includes the `Bearer` prefix, so use it as-is. Pushwoosh rejects any request where this header is missing or does not match. ![Webhook URL window showing URL and Secret fields with copy buttons for third-party service configuration](https://docs.pushwoosh.com/integrations-inbound-webhook-7.webp) 4. Click **Copy** in the **Example request** block to copy a sample `POST` request. Use it to send a test request and confirm that Pushwoosh accepts the webhook, or share it with your team as a template for the integration. ![Webhook URL window with URL, Secret, and Example request curl command for third-party service setup](https://docs.pushwoosh.com/integrations-inbound-webhook-6.webp) After you enable the webhook, it appears in the Webhooks list with an enabled status and starts accepting requests. ## Webhooks list The inbound webhook list shows every webhook in your project. Each row shows: * **Name:** webhook name. * **Status:** **Enabled** or **Disabled**. * **Received:** total number of incoming requests received by the webhook. * **Last modified:** when the webhook was last changed. ![Inbound webhook list with search, status filters, and Name Status Received Last modified columns](https://docs.pushwoosh.com/integrations-inbound-webhook-9.webp) ## Manage webhooks Open the row menu to: * **Edit settings:** opens the webhook configuration so you can change the name, event, field mapping, and user identification. * **Copy URL:** opens the **Webhook URL** window with the **URL** and **Secret** so you can copy them again. * **Activity log:** opens the request log for this webhook. * **Delete:** removes the webhook from the list. For an enabled webhook, click **Disable** to deactivate it without deleting the configuration. For a disabled webhook, click **Enable** to start accepting requests again. ### View the activity log The activity log shows all incoming requests for the selected webhook. **Summary panel** At the top, review the summary for the last 24 hours: * **Total hits:** total number of incoming requests received. * **Warning:** requests where the user was identified (or created) but at least one configured tag or event mapping failed to apply. * **Failed:** requests that were not processed at all, for example due to a bad secret or a missing identifier field. A failed request does not stop the webhook. Pushwoosh continues to accept and process later requests. | Failure reason | What it means | | :-------------------------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Auth rejected** | The shared secret does not match the webhook configuration. If five consecutive requests fail with this error, Pushwoosh sends you a notification. Update the secret to resume. No reactivation needed. | | **User identifier field missing** | The payload field mapped for user identification is not present in the request. | ![Activity log with Total hits Warning Failed summary and a request entry showing User ID Tags and Event checkmarks with JSON payload](https://docs.pushwoosh.com/integrations-inbound-webhook-10.webp) **Request entries** Each entry shows a status icon, the user identifier (for example, **User ID** or **Email**), the request timestamp, and a checkmark for each capability you configured on the webhook: **User ID** (or your chosen identifier), **Tags**, and **Event**. A checkmark means that part of the request was applied successfully; a request can show a mix of checked and failed items. Click **Show** to expand the full received JSON payload. ![Activity log request entry with user identifier, timestamp, User ID Tags and Event checkmarks, JSON payload, and Hide button](https://docs.pushwoosh.com/integrations-inbound-webhook-11.webp) ## View webhook-fired events and tags in User Explorer When a webhook request is processed successfully, Pushwoosh records the result on the matched (or newly created) user in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). Where the event appears depends on how you identified the user: * **User ID, Email, or Phone:** the event is recorded on the user profile. Open the user and go to **Events history**. * **HWID:** the event is recorded on the matching device. Open the user, find the device in **Active user devices**, and go to its **Events history** tab. Find the event by name and expand it to see the mapped attributes (for example, `price` or `products`) and the `__webhook` attribute with the webhook ID. Mapped tags appear on the same user, on the **User overview** tab, regardless of the identifier type. ![User Explorer Events history tab showing inbound webhook event with expanded attributes including __webhook ID and mapped payload fields](https://docs.pushwoosh.com/integrations-inbound-webhook-12.webp) ## Use inbound webhooks with journeys After a webhook is enabled and firing events successfully, use the selected event as a [trigger-based journey entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). When the webhook receives a valid request, Pushwoosh fires the mapped event. Any journey that uses this event as an entry trigger starts automatically for the matched user. --- # Magento integration **URL:** https://docs.pushwoosh.com/product/integrations/magento-integration/ Automatically sync customers, orders & carts from Magento 2 to Pushwoosh for real-time messaging and email automation. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. ## Integration overview [Magento](https://business.adobe.com/products/commerce.html) is a leading e-commerce platform that helps businesses manage products, customers, and orders at scale. By integrating Magento with Pushwoosh, you can automatically sync customers, orders, and abandoned carts to enable real-time messaging, targeted segmentation, and cross-channel marketing automation, helping you engage users and increase conversions directly from your store activity. ### Integration type **Destination**: This integration pushes data from your Magento store to Pushwoosh. ### Prerequisites Before proceeding, ensure the following: * Access to the Magento Admin Panel * A valid Pushwoosh account * Your Pushwoosh [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) and [App code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) * [Composer](https://getcomposer.org/) installed on your Magento server * System requirements met: * PHP 7.4 / 8.x * Magento 2.3 / 2.4 * Elasticsearch 7 * RabbitMQ ### Glossary Mapping entity names if they are different: | Magento | Pushwoosh | | :------- | :-------- | | Customer | User | ### List of synchronized entities: * Customers * Orders * Abandoned сarts ### Use cases * Send push notifications based on order creation and cart abandonment events. * Segment customers based on attributes (e.g., location, birthday) and send targeted campaigns. * Send browser-based promotional notifications. ## Set up the integration ### Installation Note To install the Pushwoosh integration, make sure you have [Composer](https://getcomposer.org/) installed. 1. Run the following Composer command in the root of your Magento project: ```shell composer require pushwoosh/magento2-integration ``` 2. Once the package is installed, run Magento setup to install the module: ```shell bin/magento setup:upgrade bin/magento setup:di:compile bin/magento cache:clean ``` ### Post-installation configuration After completing installation: 1. Log into the Magento Admin panel. 2. Navigate to **Stores > Configuration > Pushwoosh > Configuration**. ![Pushwoosh Configuration in Magento Admin Panel](https://docs.pushwoosh.com/integrations-magento-integration-1.webp) #### General settings In the **General** section, configure the following fields: * **Status**: Set this to `Enable` to activate the integration. * **API URL**: Enter the Pushwoosh API endpoint: `https://api.pushwoosh.com/` * **API Key**: Paste your Pushwoosh API Key required to authenticate requests. [Learn more about Pushwoosh API access key](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) * **App Code**: Enter the [Pushwoosh App Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) corresponding to your mobile or web application. Below are the configuration options available for syncing customers, orders, and abandoned carts. #### Order sync configuration Define how order data is synchronized between your Magento store and Pushwoosh. Fill in the following fields: | | | | --------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Order Sync Enable** | Set to **Yes** to enable automatic order synchronization. If enabled, the process will follow the defined cron schedule. Set to **No** to disable automatic synchronization. | | **Cron Settings** | Available only if Order Sync is enabled. Define when the cron job should run using the following fields: `Minute`, `Hour`, `Day`, `Month`, `Weekday`. **Cron** is a time-based scheduler used to run tasks automatically at specified intervals. | | **Order Sync Numbers** | Specify how many orders to process during each sync execution. | | **Order Sync in Real Time** | Set to **Yes** to sync orders immediately upon placement. | ![Order sync configuration settings](https://docs.pushwoosh.com/integrations-magento-integration-3.webp) Here you can also see order synchronization status: * **Total Orders**: Displays the total number of orders evaluated for synchronization. * **Synced Orders**: Number of orders successfully synchronized with Pushwoosh. * **Not Synced Orders**: Orders that have not yet been synchronized. * **Fail Synced Orders**: Orders for which synchronization attempts have failed. #### Abandoned cart sync configuration Configure synchronization of abandoned shopping cart data between your Magento store and Pushwoosh. Fill in the following fields: | | | | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Abandoned Cart Syncing** | Set to **Yes** to enable automatic synchronization of abandoned cart data. If enabled, synchronization runs according to the defined cron schedule. | | **Cron Schedule Configuration** | Define when the sync should run using the following fields: `Minute`, `Hour`, `Day`, `Month`, `Weekday`. **Cron** is a time-based job scheduler that runs tasks automatically at specified intervals. | | **Number of Abandoned Cart** | Specify how many abandoned carts will be processed during each synchronization run. | | **Minimum Inactivity Time** | Set the minimum inactivity time (in minutes) for a cart to be considered abandoned. For example, `60` means carts inactive for at least 60 minutes will be eligible for syncing. | ![Abandoned cart sync configuration settings](https://docs.pushwoosh.com/integrations-magento-integration-4.webp) Here you can also see real-time metrics on synchronization activity: * **Total Abandoned Carts**: Total number of carts identified as abandoned. * **Synced Abandoned Carts**: Number of carts successfully synchronized with Pushwoosh. * **Not Synced Abandoned Carts**: Number of carts that have not yet been synchronized. * **Fail Synced Abandoned Carts**: Number of carts for which synchronization has failed. #### Customer sync configuration Configure the synchronization of customer data from your Magento store to Pushwoosh to enable audience segmentation and personalized messaging based on customer attributes. Fill in the following fields: | | | | ---------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Customer Syncing** | Set to **Yes** to enable synchronization of all existing and new customer records. Syncing will follow the defined cron schedule and use the specified attribute mappings. | | **Cron Schedule Configuration** | Define when the sync should run using the following fields: `Minute`, `Hour`, `Day`, `Month`, `Weekday`. **Cron** is a time-based job scheduler used to run tasks automatically at specified intervals. | | **Mapping Customer Custom Fields** | Map Magento customer attributes to Pushwoosh custom fields for accurate data transfer. Examples: `Date of Birth → Birthday`, `First Name → firstName` | | **Number of Customers** | Set the maximum number of customers to sync per cron run. | ![Customer sync configuration settings](https://docs.pushwoosh.com/integrations-magento-integration-5.webp) Here you can also see synchronization statistics: * **Total Customer**: Total number of customer records identified for syncing. * **Synced Customer**: Number of customers successfully synchronized with Pushwoosh. * **Not Synced Customer**: Customers pending synchronization. * **Fail Synced Customer**: Customers that failed to sync due to errors. #### Web notifications configuration Set to `Yes` to enable web push notifications for your store visitors. Set to `No` to disable this functionality. When enabled, your Magento store can send real-time messages to users via their browser. Notifications can be used for promotional offers, order status updates, abandoned cart reminders, and other marketing communications. ![Web notifications configuration in Magento](https://docs.pushwoosh.com/integrations-magento-integration-6.webp) #### Save configuration Once all required fields are completed, click the **Save Config** button located in the top right corner. Clear Magento cache if prompted to apply the changes. ![Save Config button in Magento Admin Panel](https://docs.pushwoosh.com/integrations-magento-integration-7.webp) ## Manual sync options #### Sync selected customers to Pushwoosh If you prefer not to synchronize all customers automatically, you can manually sync individual or selected customers. 1. In the Magento Admin Panel, navigate to **Customers > All Customers**. 2. Locate and select the customer(s) you wish to sync. 3. From the **Actions** dropdown above the customer list, choose **Sync to Pushwoosh**. ![Sync to Pushwoosh action for customers](https://docs.pushwoosh.com/integrations-magento-integration-8.webp) Once synchronization is complete, the customer data will appear in the [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) section of your Pushwoosh account, with all relevant information transferred from Magento. #### Sync selected orders to Pushwoosh If you prefer not to synchronize all orders automatically, you can manually sync specific orders. For this: 1. In the Magento Admin Panel, navigate to **Sales > Orders**. 2. Select one or more orders from the list. 3. From the **Actions** dropdown, choose **Sync to Pushwoosh**. ![Sync to Pushwoosh action for orders](https://docs.pushwoosh.com/integrations-magento-integration-9.webp) After synchronization, the order will appear in the Events section of the user profile in Pushwoosh [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) as a `PW_OrderCreated` or `PW_OrderUpdated` event (if the order status has changed), including detailed order information. #### Sync selected abandoned carts To manually sync selected abandoned carts: 1. In the Magento Admin Panel, go to the **Abandoned Carts** section. 2. Select the cart you want to sync. 3. From the **Actions** dropdown, choose **Sync to Pushwoosh**. **Note:** Make sure you have configured the required inactivity period in the **Abandoned Cart Sync** settings to determine when a cart is considered abandoned. ![Sync to Pushwoosh action for abandoned carts](https://docs.pushwoosh.com/integrations-magento-integration-10.webp) After synchronization, the abandoned cart data will appear in the **Events** section of the user profile in Pushwoosh [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) as a `PW_AbandonedCart` event with detailed information. In addition, two tags will be set on the user profile: * **Abandoned Cart Date** → `PW_AbandonedCartDate` * **Abandoned Cart ID** → `PW_AbandonedCartID` These tags can be used to send abandoned cart reminders. For the full email campaign setup with the Products block and Customer Journey, see [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/). You can also use [Connected content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) in email templates. ### Verify the integration is working To confirm that the integration is working correctly: 1. Create a test customer in Magento. 2. Manually sync the customer to Pushwoosh. 3. Open [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) in your Pushwoosh dashboard and find the customer. If the customer appears in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/), it indicates that the synchronization was successful. --- # Marketo integration **URL:** https://docs.pushwoosh.com/product/integrations/marketo-integration/ Connect Marketo with Pushwoosh to automatically sync data and engage users with real-time, automated web push notifications. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. This guide explains how to integrate [Marketo](https://login.marketo.com/) with Pushwoosh. You would be able to use Web Push Notifications communication channel as a part of your marketing campaigns with Marketo. As an example, imagine a user visited your Marketo landing page but did not take an action there. You would be able to reach this user with web push notification as a reminder. This web push notification can also be personalized with Marketo data recorded for this user for the maximum engagement! Note #### HTTPS (“Secured Landing Pages”) should be enabled on your Marketo instance. While it is possible to run web push notifications with HTTP unsecured websites we strongly advise to use HTTPS secured web pages. Beginning in October 2017 with the release of Chrome 62, Google Chrome marks all unsecured web pages containing forms with the “Not secure” warning. Read more here: If you still want to go with HTTP unsecured website, follow the [Chrome/Firefox Web Push for HTTP websites](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/chrome-firefox-web-push-for-http-websites) guide. You can get FREE SSL certificate here: ## 1. Creating the Service Worker Follow this [guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30) and do the **prerequisite steps** and steps **1 - 2.2.** After completing the steps above you should have *manifest.json* and *pushwoosh-service-worker.js* files ready at hand. Switch to **Marketo Design Studio**. Marketo can only upload files to the resource folder, the folder called “Images and Files”. Upload *manifest.json* and *pushwoosh-service-worker.js* to these folder. ![Uploading manifest.json and service worker files to Marketo](https://docs.pushwoosh.com/integrations-marketo-integration-1.webp) ## 2. Creating push subscription page As it was mentioned above Marketo can only upload files to the resource folder which is different from the scope of the landing pages. For example if you click on any file uploaded above and check its URL you’ll see something like “”. However if you click on the landing page and check its URL it will be something like: “”. Note **“lp”** part in the landing pages URL’s and **“rs”** in the script URL’s. That means we need to register our *Service Worker* script in a scope different from the landing pages. To achieve that we’ll upload special push subscription page to Marketo resources folder and then open it from the landing page. Let’s create this subscription page. Get the *push-subscription.html* file from the following gist: Open the file and change the following lines: * Line 8 - change the link to the URL of *manifest.json* file uploaded in the previous step. * Line 24 - change the *scope* parameter value to the URL of your resources folder (this is the folder where *manifest.json* is). * Line 20 - change the *applicationCode* parameter value to your Pushwoosh App Id. Note the script also maps Pushwoosh user-centric push notifications with Lead Identifier of the Marketo: *userId:leadId* (line 26) Upload resulting file to the “Images and Files” folder. Now you should have these files in the folder: ![Files uploaded to Marketo Images and Files folder](https://docs.pushwoosh.com/integrations-marketo-integration-2.webp) ## 3. Creating landing page with push subscription Get the *pushwoosh-template.html* file from the following gist: Open the file and change the following lines: * Line 47 - change the *scope* parameter value to the URL of your resources folder. * Line 65 - change the link to the URL of *push-subscription.html* file uploaded in the previous step. * Line 43 - change the *applicationCode* parameter value to your Pushwoosh App Id. This script as a previous one also maps Pushwoosh user-centric push notifications with Lead Identifier of the Marketo: *userId:leadId* (line 49) Now go to the “**Templates**” section of your **Marketo landing pages**, create the template (called “pushwoosh-template” in the screenshot below) and copy-paste the contents of the resulting file there. ![Creating pushwoosh-template in Marketo Templates section](https://docs.pushwoosh.com/integrations-marketo-integration-3.webp) Now let’s create a new landing page based on this template. ![Creating new landing page based on pushwoosh template](https://docs.pushwoosh.com/integrations-marketo-integration-4.webp) In our example to show capabilities of Pushwoosh integration with Marketo campaign automation for the sake of simplicity we’ll add a simple link to the landing page. We are going to create a campaign that will trigger personal push notification when the user clicks on this link. We are going to use the same URL as the landing page. ```txt Send me an Offer! ``` ![Landing page with link for push notification offer](https://docs.pushwoosh.com/integrations-marketo-integration-5.webp) ## 4. Creating Pushwoosh Webhook Go to the **“Admin” section of Marketo** and create a new Webhook with the following parameters: * URL - * Request Type: POST Paste the following code as a template. ```plaintext { "request": { "application": "ENTER_YOUR_PUSHWOOSH_APP_ID", "auth": "ENTER_YOUR_PUSHWOOSH_API_TOKEN_HERE", "notifications": [{ "send_date": "now", "ignore_user_timezone": true, "content": "We've got a new offer for you!", "users" : ["{{lead.Id:default="ABCD-EFGH"}}"] }] } } ``` Note that we use Marketo Lead ID to refer user in Pushwoosh. See more about user-centric push notifications [here](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/push-by-user-id). ![Webhook configuration in Marketo Admin section](https://docs.pushwoosh.com/integrations-marketo-integration-6.webp) In our very simple example we use static push message. However you can use all the functionality of [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) To personalize push notification you can use **Preset** from Pushwoosh and pass the values from Marketo in **“dynamic_content_placeholders”** parameters of the `/createMessage` call for push personalization. ## 5. Integrating with Marketo automation campaign Go to the “**Marketing Activities**” section of Marketo and create a Campaign. For our example we’ll trigger the campaign when the user clicks on a link (the one we have created just two steps above) on our landing page. ![Trigger configuration for user click event in Marketo campaign](https://docs.pushwoosh.com/integrations-marketo-integration-7.webp) b. Select “send_push” Webhook in the “Flow” section ![Selecting send_push Webhook in campaign flow section](https://docs.pushwoosh.com/integrations-marketo-integration-8.webp) * Now open the landing page URL * Click on the **“Yes, I Want”** button to open push subscription dialogue * Subscribe to the push notifications * Click on the **“Send me an Offer!”** link You’ve got your Marketo automated push notification! ![Marketo automated push notification sent successfully](https://docs.pushwoosh.com/integrations-marketo-integration-9.webp) --- # Meta Ads integration **URL:** https://docs.pushwoosh.com/product/integrations/meta-ads-integration/ Connect Pushwoosh to Meta Ads. Sync Journey Builder audiences for targeting, suppression, and Custom Audience updates in Meta. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Meta Ads](https://www.facebook.com/business/ads) integration lets you sync Pushwoosh audiences to your Meta ad accounts. Use it to target or exclude users in ad campaigns and add paid ads as another channel in your customer journey. ## Use cases Use this integration to: * target high-value users in multiple channels to increase purchases or engagement * retarget users who are less responsive in other channels * build suppression audiences so loyal customers do not receive unnecessary ads ## Prerequisites Before you connect Meta Ads, make sure: * You have the **Admin** role in your Pushwoosh account. See [Manage user access and permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#creating-and-managing-roles-ex-groups) for how roles and permissions work. * You have a [**Facebook Business Manager**](https://www.facebook.com/business/tools/business-manager) set up to manage your brand’s Facebook assets, including ad accounts, pages, and apps. * You have an active [**Facebook Ad Account**](https://www.facebook.com/business/tools/ads-manager) tied to your Business Manager. * Your Facebook Business Manager admin has granted you either **Manage Campaigns** or **Manage ad accounts** permissions for the ad accounts you plan to use with Pushwoosh. * You have accepted the ad account terms and conditions for those accounts. * You have accepted [**Facebook’s Custom Audiences Terms**](https://business.facebook.com/legal/terms/customaudience) for the Facebook ad accounts you plan to use with Pushwoosh. ## Set up Meta Ads in Pushwoosh 1. In Pushwoosh, go to **Settings** > **3rd party integrations**. 2. In the Meta Ads card, click **Login page**. ![3rd-party integrations page with Meta Ads card showing Configuration, Setup guide, and Login page links](https://docs.pushwoosh.com/integrations-meta-ads-integration-1.webp) 3. Sign in to your Meta account, then click **Continue**. 4. Select the ad accounts you want to connect. ![Meta screen for choosing business access option for the connected integration](https://docs.pushwoosh.com/integrations-meta-ads-integration-6.webp) 5. Review the requested permissions for ad account and business access. 6. Click **Save**. Meta then shows a confirmation that your account is connected. ### Review connection status After setup, you will be redirected to the **Meta Ads** page in Pushwoosh. ![Pushwoosh Meta Ads page with Connected badge, ad accounts table with business account column, header actions, and How to sync audiences with Meta](https://docs.pushwoosh.com/integrations-meta-ads-integration-8.webp) The ad accounts table lists each connected account with: * **Ad account name** * **Business account** * **ID** Open the three dots at the end of a row and choose **Remove ad account** to delete that ad account from the list in Pushwoosh. ### Manage connected ad accounts On the **Meta Ads** page, click **Manage accounts** to open the dialog. Use the toggle on each row to include or exclude that ad account from the integration. Click **Apply** to save changes or **Cancel** to close without saving. To adjust the list view: * Turn **Show only connected** on or off to limit which rows appear. * Type in **Search by name or id…** to find accounts in the list. ![Manage ad accounts dialog with Show only connected toggle, search by name or id, row toggles with Connected or Disconnected badges, Cancel and Apply](https://docs.pushwoosh.com/integrations-meta-ads-integration-4.webp) ### Map project tags to Meta fields Mapping user properties allows you to tell Pushwoosh which Meta user attributes should update which **Tag name** fields in your project. That way, when data comes from Meta, it is saved where you expect. Note For audience sync, Pushwoosh always sends one identifier per user from **Email**, **Phone number**, or **MADID**, depending on what exists on the profile. Configure mapping when you want Meta to receive **additional** user attributes beyond the identifiers above. 1. On the **Meta Ads** page, click **Map user data**. 2. For each **Facebook field** in the left column, choose a **Tag name** in your project from the control on the right. Map only the rows you need. ![Map project tags to Meta fields modal with Facebook field and Tag name columns, overwrite checkbox, Cancel and Save](https://docs.pushwoosh.com/integrations-meta-ads-integration-3.webp) Automatically mapped fields Pushwoosh maps these fields automatically. You do not set them in **Map project tags to Meta fields**: * **Email** * **Phone number** * **MADID** 3. Click **Save** to apply the mapping or **Cancel** to close without saving. ## Enable MADID collection in the SDK Meta Ads matches users using device identifiers (MADID) collected via the mobile SDK. The Pushwoosh SDK does not collect advertising identifiers (GAID on Android, IDFA on iOS) automatically. Both platforms require explicit user consent before the identifier can be read. In your application, request user consent, read the identifier when permitted, and pass the value to the SDK. * Android **1. Add the dependency** ```groovy implementation 'com.google.android.gms:play-services-ads-identifier:...' ``` **2. Declare the AD_ID permission (required for targetSdk ≥ 33)** Add this to your `AndroidManifest.xml`: ```xml ``` Caution Without this permission on Android 13+, `AdvertisingIdClient.getAdvertisingIdInfo()` silently returns a zeroed UUID (`00000000-0000-0000-0000-000000000000`). The Pushwoosh SDK normalizes that to `null`, so no MADID is sent to the server and Meta audience matching will not work. **3. Retrieve the GAID and pass it to the SDK** `getAdvertisingIdInfo` must be called on a background thread: ```java String gaid = AdvertisingIdClient.getAdvertisingIdInfo(context).getId(); Pushwoosh.getInstance().setAdvertisingId(gaid); ``` To clear the stored value on the backend, pass `null` or an empty string: ```java Pushwoosh.getInstance().setAdvertisingId(null); ``` **Behavior notes:** * If the value has not changed since the last successful call, no network request is made. * If the network request fails, retry on the next app launch. * The call is ignored when `Pushwoosh.stopCommunication()` is active. * The zero UUID (`00000000-0000-0000-0000-000000000000`) is treated the same as `null` — the stored MADID is cleared on the backend. * iOS **1. Add the usage description to `Info.plist`** Apple requires this key before showing the ATT permission dialog: ```xml NSUserTrackingUsageDescription We use your advertising identifier to show you relevant ads. ``` **2. Declare the tracking domain in your privacy manifest** If your app uses IDFA for tracking, Apple requires you to list the domains that receive tracking data in your [privacy manifest](https://developer.apple.com/documentation/bundleresources/privacy-manifest-files) (`PrivacyInfo.xcprivacy`). See [TN3182](https://developer.apple.com/documentation/technotes/tn3182-adding-privacy-tracking-keys-to-your-privacy-manifest) for the full requirements. Set `NSPrivacyTracking` to `true` and add the Pushwoosh tracking domain to `NSPrivacyTrackingDomains`: ```xml NSPrivacyTracking NSPrivacyTrackingDomains tracking.svc-nue.pushwoosh.com ``` Note If the user has not granted ATT permission, iOS blocks network requests to all domains listed in `NSPrivacyTrackingDomains`. The MADID will not be sent regardless of what your code does. **3. Request tracking authorization and pass the IDFA to the SDK** `ATTrackingManager` requires iOS 14 or later. If your deployment target is below iOS 14, wrap the call in an availability check. The Pushwoosh SDK does not call `ATTrackingManager`. Request tracking authorization in your application, then pass the result to the SDK: ```swift import AppTrackingTransparency import AdSupport if #available(iOS 14, *) { ATTrackingManager.requestTrackingAuthorization { status in let idfa = status == .authorized ? ASIdentifierManager.shared().advertisingIdentifier.uuidString : nil Pushwoosh.configure.setAdvertisingId(idfa) } } ``` To clear the stored value on the backend, pass `nil` or an empty string: ```swift Pushwoosh.configure.setAdvertisingId(nil) ``` **Behavior notes:** * If the value has not changed since the last successful call, no network request is made. * If the network request fails, call `setAdvertisingId` again on the next app launch. * The call is ignored when `Pushwoosh_ALLOW_SERVER_COMMUNICATION` is disabled. * The zero UUID (`00000000-0000-0000-0000-000000000000`) is treated the same as `nil` or an empty string — the stored MADID is cleared on the backend. > Call `requestTrackingAuthorization` from your app’s main UI flow. Apple recommends doing this after showing your own explanatory screen, not immediately on launch. ### How it works Once you call `setAdvertisingId`, the SDK sends the value to the Pushwoosh tracking endpoint as the `madid` field alongside the app code and device hardware ID. Pushwoosh uses this identifier to match your device records with Meta Ads audiences for synchronization. ## Sync audiences in journeys The **Audience sync** point in **Journey Builder** links your journey to a Meta Custom Audience. Every time a user reaches that point, Pushwoosh asks Meta to either add them to the audience or remove them from it. For example, you may use this to stop showing a webinar ad to users who already registered, so you don’t waste ad spend on people who don’t need to see it anymore. To configure audience sync: 1. Open [**Journey Builder**](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). 2. Add an [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). In **Audience source**, choose a Pushwoosh segment or list that defines who enters this journey. For example, a segment **Users with tag `webinar_registered` set to `true`**. Only those users will move through the journey and reach **Audience sync**. 3. Add the **Audience sync** point. 4. Under **How to sync users info to the ad audience**, pick one option: * **Add users to audience**. Adds each user who reaches this step to the audience you select. For example, use this to start showing an ad to users who signed up but have not yet attended. * **Remove users from audience**. Removes each user who reaches this step from that audience. In this example, select this option to stop showing the webinar ad to users who already registered. 5. In **AD PLATFORM**, choose **Meta Ads**. To sync to a Google Ads audience instead, see [Google Ads integration](https://docs.pushwoosh.com/product/integrations/google-ads-integration/#sync-audiences-in-journeys). 6. In **Meta Ads account**, select the connected ad account. 7. In **Audience**, select the Meta audience, for example **Webinar**. ![Audience sync panel with Audience dropdown and selected Meta Custom Audience](https://docs.pushwoosh.com/integrations-meta-ads-integration-10.webp) 8. Click **Apply** to save the point or **Cancel** to close without saving. 9. Finish configuring the journey, then launch it. ![Audience sync panel with step name, add or remove users, Meta Ads account, Audience, Apply and Cancel](https://docs.pushwoosh.com/integrations-meta-ads-integration-9.webp) When those users reach **Audience sync**, they are removed from the **Webinar** audience in Meta, so they no longer see the webinar ad there. ## Behavior and error handling Journey processing depends on Meta account and audience availability: * Meta updates the audience only when it can match the user from the data Pushwoosh provides. If Meta cannot match the user, the audience does not change for that user, and they continue in the journey. * If a profile reaches the **Audience sync** point while the connected ad account is disconnected, the journey stops for that profile and Pushwoosh sends system and email notifications. * If a selected audience is not found in Meta and the API returns an error, the journey stops for that profile and Pushwoosh sends system and email notifications. ## Audience sync statistics After launch, open statistics for the **Audience sync** step to see entry volume, adds and removals, and skipped profiles. For metric details, see [**Audience sync**](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#audience-sync) in **Customer Journey statistics**. ![Audience sync statistics with Total entries, Added to Meta audience, Removed from Meta audience, Skipped not synced moves to next step, Export users, and Meta Ads account for sync](https://docs.pushwoosh.com/integrations-meta-ads-integration-11.webp) --- # Mixpanel integration **URL:** https://docs.pushwoosh.com/product/integrations/mixpanel-integration/ Set up Mixpanel integration to build highly accurate segments for precise targeting --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Mixpanel](https://mixpanel.com/) is a tool that helps you understand how your users interact with your app. Being integrated with Pushwoosh, it allows building sophisticated audience segments and using these segments for exceptionally relevant and perfectly targeted communications. To import the segments you build in [Mixpanel](https://mixpanel.com/login/) to Pushwoosh, you have to integrate both tools in a few steps: 1. Integrate Pushwoosh SDK 2. Configure platforms in Pushwoosh 3. Set the integration in Mixpanel ## 1. Integrate Pushwoosh SDK Note Skip this step if you’ve already integrated the Pushwoosh SDK into your app or website. First, integrate the Pushwoosh SDK into your mobile or web project: * [Setting up Pushwoosh iOS SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk) * [Pushwoosh Android SDK integration](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk) * [Web Push SDK 3.0](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30) ## 2. Configure platforms in Pushwoosh Note Skip this step if you’ve already configured platforms for your project. Configure platforms for sending messages via Pushwoosh following the guides for instructions: * [Configure iOS](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration) * [Configure Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-firebase-configuration) * [Configure Chrome and Firefox](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/chrome-configuration) ## 3. Configure Pushwoosh for Mixpanel ### Get your Pushwoosh project’s credentials For setting the integration, you’ll need the **app code** and **API access token** of the Pushwoosh project. #### App code Go to the project you’re going to import segments to, **click its name in the top left** and copy the app code. ![Project dropdown in the sidebar showing the project name with its app code below](https://docs.pushwoosh.com/integrations-mixpanel-integration-1.webp) #### Auth token Go to **Settings -> API Access** and copy the **API auth token** applicable to the project you’ve chosen. ![Copying API auth token from Pushwoosh settings](https://docs.pushwoosh.com/integrations-mixpanel-integration-2.webp) ### Set Pushwoosh integration in Mixpanel 1. [Log in to Mixpanel](https://mixpanel.com/login/). 2. Go to **Data Management -> Integrations -> Pushwoosh**. 3. Click **Connect**. 4. In the window opened, set the **Connector name**, then paste the Pushwoosh **API Access Token** and **App Code** you’ve copied at previous step. ![Connecting Pushwoosh integration in Mixpanel](https://docs.pushwoosh.com/integrations-mixpanel-integration-3.webp) 5. To match Mixpanel User with Pushwoosh User, **set the $pushwoosh_user_id user profile property** with UserIDs or HWIDs collected from Pushwoosh. Please refer to [Mixpanel docs](https://help.mixpanel.com/hc/en-us/articles/115004501966-User-Profiles) for more detail. If a cohort member has no Pushwoosh user ID, Pushwoosh uses the member’s Mixpanel distinct ID instead. Members with neither are skipped. ## 4. Test your integration To export Mixpanel cohorts to your Pushwoosh account: 1. Open your cohorts: **Data Management -> Cohorts**. 2. Search for the cohort you’d like to export. Click the ”**…**” button next to the cohort you’d like to export. In the context menu opened, select **Export to -> Pushwoosh**. 3. Then, specify whether the cohort should be sent to Pushwoosh **one time** or be **synced dynamically** at predefined intervals, updating the Pushwoosh segment to the most current set of users from Mixpanel. 4. Once everything’s set up, the cohort you’ve exported will **appear in the Segments (Filters)** section of your Pushwoosh Control Panel as `Mixpanel_`. ### How cohorts appear in Pushwoosh Pushwoosh creates one segment for each cohort you export and keeps it in sync as follows: * Pushwoosh creates a segment named `Mixpanel_` the first time it receives cohort members that can be matched. If none of the members in a sync have a Pushwoosh user ID or a Mixpanel distinct ID, no segment is created. * Cohort membership is stored as the cohort ID in the [CSV import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#csv-import-tag) tag of each matched user. For Mixpanel cohorts this is a list tag that can hold several cohort IDs. The segment selects users whose **CSV import** tag contains that ID. * With dynamic sync, Mixpanel sends users who joined or left the cohort, and Pushwoosh adds or removes the cohort ID accordingly. * A one-time export (full sync) only adds users. If you export the cohort again, users who left it in the meantime stay in the Pushwoosh segment. To keep the segment current, choose **synced dynamically** in step 3 of [Test your integration](https://docs.pushwoosh.com/product/integrations/mixpanel-integration/#4-test-your-integration). * Pushwoosh finds the segment by name. If you rename the cohort in Mixpanel, the next sync creates a new `Mixpanel_` segment, and the old one stays in Pushwoosh. If you don’t need the old segment, delete it manually. See [Delete segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#delete-segment). Deleting a segment pauses the journeys that use it and cancels messages scheduled to it. --- # mParticle integration **URL:** https://docs.pushwoosh.com/product/integrations/mparticle-integration/ Use mParticle data to create targeted, personalized, and effective campaigns in Pushwoosh --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. ## Integration overview [mParticle](https://www.mparticle.com/) is a platform for managing customer data collected from mobile apps, web, and other sources. If you use mParticle to store customer data, you can easily set up integration with Pushwoosh. This will allow you to create targeted, personalized, and effective campaigns in Pushwoosh using data from mParticle. You can send the following data from mParticle to Pushwoosh: * **User attributes**: name, location, device, preferences, behavior, etc. This data will be saved in Pushwoosh as [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) and their values for each user. The data type for each Tag is identified automatically. * **Events**: each Event configured in mParticle will be transferred to Pushwoosh and saved as the [Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) with the same name and tags if there are any. Pushwoosh will also get information about which Events each user has triggered. * **Segments**: all mParticle Audiences based on user attributes and triggered Events can be transferred to Pushwoosh as Segments. You can use them to create highly targeted campaigns. If an Audience segment is updated in mParticle, that segment will also be updated in Pushwoosh. The update frequency of mParticle segments is configured on the mParticle side. Note Please note that Tags and Events in mParticle should not be named the same as the default Tags and Events in Pushwoosh. If the names match, the data may not be transmitted. The data is not transferred back from Pushwoosh to mParticle, but we plan to add this option in the future, so stay tuned. ## Setting up integration Note To set up mParticle and Pushwoosh integration, you do not need to integrate the Pushwoosh SDK into your app. This is only required if you create campaigns that include in-app messages. 1. [In the mParticle interface](https://app.mparticle.com/login), go to the **Directory** section and find **Pushwoosh** in the list. Hover over the icon and click **Setup**. ![Finding Pushwoosh in mParticle Directory](https://docs.pushwoosh.com/integrations-mparticle-integration-1.webp) 2. Select both **Event** and **Audience** Outputs and click **Configure**: ![Selecting Event and Audience Outputs in mParticle](https://docs.pushwoosh.com/integrations-mparticle-integration-2.webp) 3. On the page that opens (**Setup** → **Outputs**), click on the plus icon in the **Pushwoosh Default Group** row: ![Pushwoosh Default Group in mParticle Outputs](https://docs.pushwoosh.com/integrations-mparticle-integration-3.webp) Insert any configuration name (for example, *Pushwoosh integration*), the Pushwoosh application code, and the API key. Click **Save**. Where can I find the application code and API key? You can find the application code at the top left corner of the Pushwoosh interface, right below the project name: ![Finding application code in Pushwoosh interface](https://docs.pushwoosh.com/integrations-mparticle-integration-4.webp) To find the API key, go to **Settings** → **API Access**: ![API Access section in Pushwoosh settings](https://docs.pushwoosh.com/integrations-mparticle-integration-5.webp) ![Configuration settings for Pushwoosh integration](https://docs.pushwoosh.com/integrations-mparticle-integration-6.webp) Now switch to the **Audience** tab and repeat these steps for the **Pushwoosh Default** row: ![Audience tab configuration in mParticle](https://docs.pushwoosh.com/integrations-mparticle-integration-7.webp) 4. Go to **Connections** → **Connect** and click on the Input you want to integrate with Pushwoosh. ![Connections section in mParticle interface](https://docs.pushwoosh.com/integrations-mparticle-integration-8.webp) Click **Connect Output**, select **Pushwoosh**, and activate the connection. ![Connecting Pushwoosh output in mParticle](https://docs.pushwoosh.com/integrations-mparticle-integration-9.webp) Repeat these steps for each Input you want to integrate with Pushwoosh. If everything is configured correctly, Pushwoosh will start receiving data from mParticle, and you will be able to create [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) and send [push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/) using customer data from mParticle. --- # Piano Analytics integration **URL:** https://docs.pushwoosh.com/product/integrations/piano-analytics-integration/ Integrate Pushwoosh with Piano Analytics to enhance user behavior insights and create precise segments for targeted marketing campaigns. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Piano Analytics](https://piano.io/product/analytics/) is a tool that helps businesses understand how people interact with their websites and apps. It collects data about user behavior and provides insights through reports and visualizations. This information helps businesses make better decisions about how to improve their digital products and marketing efforts. Integrating [Piano Analytics](https://piano.io/product/analytics/) and Pushwoosh allows for detailed user behavior insights to create precise segments and send highly targeted push notifications, emails, and in-app messages to the right users at the right time. ## Integration overview ### Integration type * **Destination**: Data is sent from Piano Analytics to Pushwoosh. * **Source**: Data is sent from Pushwoosh to Piano Analytics. ### Prerequisites Paid accounts in both Pushwoosh and Piano Analytics ### Glossary (mapping entity names if they are different) When integrating Pushwoosh and Piano Analytics, certain entity names may vary between the two platforms. Here are the mappings between Pushwoosh and Piano Analytics: | Pushwoosh | Piano Analytics | | ----------------------------------------------- | ----------------------------------------------------------------------------------- | | Piano Visitor ID tag | visitor ID (`idclient`) | | PostEvent.EventName | event name | | PostEvent.UserID | `pushwooshid` (sent as `pushwooshID`, Piano lowercases property keys) | | PostEvent.RichMediaID | `src_medium` | | PostEvent.CampaignID | `src_campaign` | | PostEvent.PlatformID | `rendering_platform` (`iOS App`, `android`, `web`, or `pwa` for any other platform) | | Browser Type tag | `device_type` (`Mobile Phone` or `Desktop`) | | Country tag | `geo_country` (full country name) | | OS Version, Device Model, and Browser Type tags | User-Agent and Client Hints request headers | ### Synchronized entities **Source** * Events * Push Stats (as the `PW_NotificationSend` and `PW_NotificationOpen` events) * Tags (as event properties and request headers) **Destination** * Segments ### Use cases **Unified analytics** Combine Pushwoosh engagement data with Piano Analytics for comprehensive user insights. This allows you to get a holistic view of user behavior and interactions across multiple touchpoints. **Segmentation** Create targeted user segments in Pushwoosh based on Piano Analytics data. Use this enhanced segmentation to deliver personalized and effective marketing campaigns. **Performance Monitoring** Track campaign effectiveness and user interaction across platforms. ### How does the integration work? ##### Flow Overview 1. **Setup**: Configure integration settings in both Pushwoosh and Piano Analytics accounts. 2. **Event Selection**: Choose which Pushwoosh system events are sent to Piano. Events keep their names in Piano. 3. **Data Transfer**: Pushwoosh sends the selected events to Piano Analytics. 4. **Segment Import**: Piano Analytics sends segment data to Pushwoosh for audience segmentation. ##### Technical Flow 1. **Post Events** * Pushwoosh sends event data to Piano Analytics. * Event properties are mapped as shown in the [glossary](https://docs.pushwoosh.com/product/integrations/piano-analytics-integration/#glossary-mapping-entity-names-if-they-are-different). 2. **Push stats**: * Push sends and opens reach Piano as the system events `PW_NotificationSend` and `PW_NotificationOpen` if you select them. * Delivery status is not sent. 3. **Piano VisitorID Integration**: * Pushwoosh sends events only for users whose device has the **Piano Visitor ID** tag. Set it as described in [Mapping Piano VisitorID to Pushwoosh UserID/HWID](https://docs.pushwoosh.com/product/integrations/piano-analytics-integration/#mapping-piano-visitorid-to-pushwoosh-useridhwid-for--accumulated-analytics-across-sdks). ## Setting up the integration To set up the integration, go to **Settings > 3rd Party Integrations** in your Pushwoosh account and locate **Piano**. Click **Configure**. ![Piano integration settings in Pushwoosh](https://docs.pushwoosh.com/integrations-piano-analytics-integration-1.webp) In the window that opens, fill in the following settings: 1. Site ID To locate your Site ID, log in to your Piano account and go to **Analytics > Data Collection Portal > Dashboard.** Find the relevant website and copy its ID. ![Finding Site ID in Piano Analytics](https://docs.pushwoosh.com/integrations-piano-analytics-integration-2.webp) 2. Collection Domain To find the Collection Domain, navigate to **Tagging > Collection Domains**. Copy the Collection Domain ID. Make sure to enter the collection domain in the ******.pa-cd.com** format without the **https:// prefix.** ![Collection Domain settings in Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-3.webp) 3. Piano API Access and Secret keys To get the keys: * In your Piano account, click on **See profile** below your account name. ![Account profile menu in Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-4.webp) * Navigate to the **API Keys** tab and create a new key with a clear name and description. Then click **OK**. ![Creating API key in Piano account](https://docs.pushwoosh.com/integrations-piano-analytics-integration-5.webp) * In the window that opens, copy your **Access Key** and **the Secret Key**. Caution For security reasons, the **Secret Key** will only be displayed once. Please make sure you save both keys in a safe place before continuing. ![Copying API keys from Piano account](https://docs.pushwoosh.com/integrations-piano-analytics-integration-6.webp) * In the **API key** field in Pushwoosh, enter both the **Access Key** and **Secret Key**, separated by an underscore. For example: **accesskey_secretkey**. ![Entering API keys in Pushwoosh integration](https://docs.pushwoosh.com/integrations-piano-analytics-integration-7.webp) 4. Events In **Events**, select at least one event to send to Piano. Only these Pushwoosh system events are available: `PW_DeviceRegistered`, `PW_DeviceUnregistered`, `PW_ApplicationOpen`, `PW_ApplicationMinimized`, `PW_ScreenOpen`, `PW_SiteOpened`, `PW_InAppPurchase`, `PW_NotificationSend`, `PW_NotificationOpen`, and `PW_EmailLinkClicked`. 5. Unique events (optional) Turn on **Unique events enabled**, set **Timeframe** in hours (from 1 to 168), and select events in **Unique Events**. For each user, Pushwoosh then also sends a `_unique` event, for example `PW_SiteOpened_unique`, at most once within the timeframe. The timeframe is shared by all unique events of a user. For example, if both `PW_ApplicationOpen` and `PW_SiteOpened` are unique and the timeframe is 24 hours, a user who opens the app and then the site on the same day gets only `PW_ApplicationOpen_unique`. ### Creating Piano events to match Pushwoosh events To track events sent from Pushwoosh in Piano, you need to create corresponding events manually within Piano. If you use unique events, also create a `_unique` event for each of them, for example `PW_SiteOpened_unique`. Here’s how to do it in your Piano account: 1. Go to **Settings > Data management > Data model**. 2. Select **Events** and click the plus icon to create a new event. 3. In the window that opens, provide the event name and description, for example, ***“pw_siteopened”***. Note The naming of events is not case-sensitive. If you create an uppercase name for an event in Pushwoosh, for example, *PW_SiteOpened*, it will match the *pw_siteopened* event in Piano. 4. In the **Type** field, select **On-site**. 5. In the **Categories** field, select **Events**. 6. Click **Save**. ### Setting your website’s time zone in Piano Setting the correct time zone for your website in Piano is crucial for accurate data tracking and analysis. The best practice is to match the time zone with the location where you’re sending events and conducting analysis. This eliminates time discrepancies. To edit the timezone in Piano, go to **Dashboard** and click the line containing your site information. In the pop-up window, click **Edit** and select the desired time zone. Then, click **Save**. ![Setting timezone for website in Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-9.webp) ### Creating a property for identifying events sent from Pushwoosh in Piano To make data management easier, create a property in Piano that can be associated with Pushwoosh events. This will allow you to easily identify events sent from Pushwoosh. Here’s how to create the property: 1. In Piano, go to **Settings > Data Management > Properties.** 2. Click **Create Property**. 3. In the **Name** field, enter **pushwooshid**. 4. In the **Property Key** field, enter **pushwooshid**. 5. Set the **Type** to **String**. 6. Click **Save**. ![Creating property for Pushwoosh events in Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-10.webp) ### Checking if Pushwoosh events are sent to Piano To verify that events set up in Pushwoosh are successfully sent to Piano, follow these steps: 1. In your Piano account, go to **Explorer > Audience > Overall Traffic**. 2. Create a segment based on events where the property **pushwooshid** is not empty. ![Creating segment based on events in Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-11.webp) The filtered view will display overall traffic information, including any associated events. ![Overall traffic view with Pushwoosh events](https://docs.pushwoosh.com/integrations-piano-analytics-integration-12.webp) For a more granular view of events sent from Pushwoosh, navigate to **Content > Events**. This section provides a detailed breakdown of all the events sent from Pushwoosh, including the number of times each event occurred. ![Detailed breakdown of events from Pushwoosh](https://docs.pushwoosh.com/integrations-piano-analytics-integration-13.webp) ### Creating segments in Piano Once you’ve completed the integration setup, you can start analyzing events sent from Pushwoosh within Piano. To access these events, navigate to **Explorer > Content > Events** in your Piano account. ![Events view in Piano Analytics](https://docs.pushwoosh.com/integrations-piano-analytics-integration-14.webp) Then [create Segments](https://support.piano.io/hc/en-us/articles/4467486423570-Create-a-segment) using these events. When creating segments, select the **`pushwooshid`** property you’ve created earlier to filter events. ![Creating segments with pushwooshid property](https://docs.pushwoosh.com/integrations-piano-analytics-integration-15.webp) Make sure to give your segment a clear and descriptive name. The **Segment Key**, which is a unique identifier for the saved segment, will be needed later for sending segments to Pushwoosh. ![Naming segment from Piano](https://docs.pushwoosh.com/integrations-piano-analytics-integration-16.webp) ### Sending segments from Piano to Pushwoosh To enable Pushwoosh to retrieve segments from Piano, provide Pushwoosh with the SegmentKey you created. To find the SegmentKey for a segment, go to **Analysis > Library > Segments**. Click on the segment you need and copy the **Segment Key**. ![Choosing segment to send from Piano to Pushwoosh](https://docs.pushwoosh.com/integrations-piano-analytics-integration-17.webp) In the Pushwoosh Integrations section, locate **Piano** and click **Import segment**. Then, enter the Segment key you copied. ![Importing segments from Piano to Pushwoosh via integration](https://docs.pushwoosh.com/integrations-piano-analytics-integration-18.webp) If the import is successful, you’ll see a confirmation message. ![Segment imported from Piano to Pushwoosh](https://docs.pushwoosh.com/integrations-piano-analytics-integration-19.webp) You can access your imported segments in Pushwoosh. Once the import is complete, they’ll be listed in the **Audience > Segments** section of your Pushwoosh account as ` (Piano Autogenerated)`. Import is a one-time snapshot of users who matched the segment in Piano from the same date of the previous month up to today. Pushwoosh adds the segment key to the **Piano Segments** list tag of matched devices. Only users whose events carry `pushwooshid` are matched. Users who leave the segment in Piano later keep the tag. Importing the segment again adds the key to newly matched devices and does not remove it from devices tagged earlier. ![Imported segments from Piano in Pushwoosh](https://docs.pushwoosh.com/integrations-piano-analytics-integration-20.webp) ## Mapping Piano VisitorID to Pushwoosh UserID/HWID for accumulated analytics across SDKs If a client wants to accumulate analytics from both the Pushwoosh SDK and the Piano Analytics SDK, Pushwoosh needs to map Piano VisitorID to Pushwoosh UserID/HWID. The solution is to send a Piano VisitorID tag to Pushwoosh for the user/device. For this, do the following: 1. Create a tag named ‘**Piano Visitor ID**’ on the Pushwoosh side for the application. Note: The tag name must be exactly as described. 2. On the client’s application side, get the Piano Visitor ID within the Piano SDK using the **getVisitorId** method. 3. On the client’s application side, use the Pushwoosh SDK to call the `setTags` request with the tag value once. If the tag value (Piano Visitor ID) exists for the user/device, Pushwoosh will automatically include it as Client ID in requests to Piano Analytics. On the Piano Analytics side, it will be transformed into a unique visitor ID value. If the tag is empty, Pushwoosh does not send the user’s events to Piano. ## Code examples ##### Web Push SDK (JavaScript) ```javascript Pushwoosh.push(function(api) { // Set tags for a user api.setTags({ 'Piano Visitor ID': pa.getVisitorID(), }); ``` ##### Android (Java) ```java String value1 = PianoAnalytics.getInstance().visitorId(); TagsBundle pianoTag = new TagsBundle.Builder() .putString("Piano Visitor ID", value1) .build(); Pushwoosh.getInstance().setTags(pianoTag); ``` ##### iOS (Swift) ```swift var pianoId: String = pa.getVisitorId(completionHandler: { visitorId in Pushwoosh.sharedInstance().setTags([ "Piano Visitor ID": visitorId ]) }) ``` ##### iOS (Objective-C) ```objective-c __block NSString *pianoId; [pa getVisitorIdWithCompletionHandler:^(NSString *visitorId) { Piano Visitor ID = visitorId; [[Pushwoosh sharedInstance] setTags:@{@"Piano Visitor ID": visitorId}]; }]; ``` In general, Pushwoosh uses device tags to provide device and other information to Piano Analytics within each request. Here is a list of common tags used: * **Country** * **Device Model** * **OS Version** * **Browser Type** (in some cases) ## How to verify the integration is working #### Source: Detecting events sent to Piano Analytics There are two easy ways to detect events that were sent to Piano Analytics: ##### Stream Inspector 1. Go to your **Piano Analytics** account. 2. Navigate to **Settings** > **Data Collection Portal** > **Tools** > **Stream Inspector**. 3. Choose the connected site you want to check. 4. Select the events you want to inspect and start a session. 5. Verify that the events were received by Piano Analytics. ##### Check Events in Overall Traffic 1. Go to your **Piano Analytics** account. 2. Navigate to **Settings** > **Explorer** > **Audience** > **Overall Traffic**. 3. Choose the site and date range in the menu below. #### Destination: Importing Piano’s Segment To verify the integration on the destination side: * Import Piano’s segment. * You should see a new segment (filter) with a name constructed like `segment_key (Piano Autogenerated)`. * Recalculate the segment to get up-to-date information about current devices processing. ## FAQ troubleshooting: Common issues #### How can I ensure events are correctly sent from Pushwoosh to Piano Analytics? **Solution**: Use Piano’s Stream Inspector or check Overall Traffic in Piano Analytics to monitor incoming events from Pushwoosh. #### What should I do if segment data isn’t imported correctly into Pushwoosh? **Solution**: Ensure the Segment Key from Piano matches the setup in Pushwoosh. Check for any errors in segment size calculations or data retrieval. #### Can I integrate Piano VisitorID with Pushwoosh for enhanced analytics? **Solution**: Yes, by setting up a specific tag in Pushwoosh and using Piano’s SDK to fetch VisitorID, you can synchronize user IDs across platforms. #### What should I do if there are issues with API key configurations? **Solution**: Review API request logs, ensure correct endpoint URLs and authentication tokens (API keys), and check for any server-side errors reported by either service. --- # Qonversion integration **URL:** https://docs.pushwoosh.com/product/integrations/qonversion-integration/ Leverage Pushwoosh integration with Qonversion to enhance customer retention and revenue growth --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Qonversion](https://qonversion.io/) is a data platform that can help you increase your in-app subscription revenue. The platform makes it easy to integrate in-app subscriptions and conduct monetization tests. Qonversion offers the necessary backend infrastructure to validate user receipts and facilitate cross-platform access to paid content within your app, eliminating the need for you to develop your server. Plus, you can take advantage of the extensive subscription analytics and seamless integrations with leading marketing, attribution, and product analytics platforms. ## Use cases Qonversion sends [subscription events](https://documentation.qonversion.io/docs/integrations-overview#tracked-events), including purchases, subscriptions after free trials, renewals, refunds, etc. to Pushwoosh. This enables you to actively connect with users to enhance customer retention and ultimately drive higher revenue for your business. ### Subscription renewal reminders You can use Qonversion’s data about expired subscriptions to create a customer journey that automatically sends personalized reminders through push notifications or in-app messages. ### Personalized offers Use subscription data from Qonversion to target users who canceled their subscriptions and create a customer journey that starts with a compelling push notification offering an exclusive discount. If the user engages with the push notification but doesn’t complete the purchase, the journey can then send an in-app message with additional perks, encouraging them to return and redeem the offer. ### Trial conversion Use Qonversion to track users whose trial period is about to end and create customer journeys to encourage them to convert into paying subscribers. ## Setting up the integration ### Setup the SDKs 1. Install the Pushwoosh SDK. Learn more about how to install Pushwoosh SDK on [IOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk), [Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk), and [Cross-platform](https://docs.pushwoosh.com/developer/pushwoosh-sdk/cross-platform-frameworks). 2. Follow the [Qonversion SDK installation guide](https://documentation.qonversion.io/docs/install-sdk) to set up the Qonversion SDK. 3. To attribute events sent from [Qonversion](https://dash.qonversion.io/site/login) to a specific Pushwoosh user, set the Pushwoosh Hardware ID (HWID): * Swift ```swift Qonversion.shared().setUserProperty(.pushWooshHwId, value: Pushwoosh.sharedInstance().getHWID()) ``` * Objective-C ```objective-c [[Qonversion sharedInstance] setUserProperty:QONUserPropertyKeyPushWooshHwId, value: [[Pushwoosh sharedInstance] getHWID]]; ``` * Java ```java Qonversion.getSharedInstance().setUserProperty(QUserPropertyKey.PushWooshHwId, Pushwoosh.getInstance().getHWID()); ``` * Kotlin ```kotlin Qonversion.shared.setUserProperty(QUserPropertyKey.PushWooshHwId, Pushwoosh.getInstance().getHWID()) ``` - Flutter ```plaintext String hwid = await Pushwoosh.getInstance.getHWID; Qonversion.getSharedInstance().setUserProperty(QUserPropertyKey.pushWooshHwId, hwid); ``` - React Native ```plaintext Pushwoosh.getHwid( function(hwId) { Qonversion.getSharedInstance().setUserProperty(UserPropertyKey.PUSH_WOOSH_HW_ID, hwId); } ); ``` - Unity ```plaintext Qonversion.GetSharedInstance().SetUserProperty(UserPropertyKey.PushWooshHwId, Pushwoosh.Instance.HWID); ``` - Cordova ```plaintext pushwoosh.getPushwooshHWID( function(hwId) { Qonversion.getSharedInstance().setUserProperty(Qonversion.UserPropertyKey.PUSH_WOOSH_HW_ID, hwId); } ); ``` 4. (Optional) For improved attribution accuracy, set the same user ID for both Pushwoosh and Qonversion * Swift ```swift Pushwoosh.sharedInstance.setUserId("yourSideUserID") Qonversion.shared().setUserProperty(.pushwooshUserId, value: "yourSideUserID") ``` * Objective-C ```objective-c [[Pushwoosh sharedInstance] setUserId:@"yourSideUserID"]; [[Qonversion sharedInstance] setUserProperty:QONUserPropertyKeyPushwooshUserId, value: @"yourSideUserID"; ``` * Java ```java Pushwoosh.getInstance().setUserId("yourSideUserID"); Qonversion.getSharedInstance().setUserProperty(QUserPropertyKey.PushwooshUserId, "yourSideUserID"); ``` * Kotlin ```kotlin Pushwoosh.getInstance().setUserId("yourSideUserID") Qonversion.shared.setUserProperty(QUserPropertyKey.PushwooshUserId, "yourSideUserID") ``` - Flutter ```plaintext Pushwoosh.getInstance.setUserId('yourSideUserID'); Qonversion.getSharedInstance().setUserProperty(QUserPropertyKey.pushwooshUserId, 'yourSideUserID'); ``` - React Native ```plaintext Pushwoosh.setUserId('yourSideUserID'); Qonversion.getSharedInstance().setUserProperty(UserPropertyKey.PUSH_WOOSH_USER_ID, 'yourSideUserID'); ``` - Unity ```plaintext Pushwoosh.Instance.SetUserId("yourSideUserID"); Qonversion.GetSharedInstance().SetUserProperty(UserPropertyKey.PushwooshUserId, "yourSideUserID"); ``` - Cordova ```plaintext pushwoosh.setUserId('yourSideUserID'); Qonversion.getSharedInstance().setUserProperty(Qonversion.UserPropertyKey.PUSH_WOOSH_USER_ID, 'yourSideUserID'); ``` ### Configure the Pushwoosh integration 1. Navigate to your [Pushwoosh dashboard](https://app.pushwoosh.com/projects) to get the following: * **Pushwoosh Application Code** (XXXXX-XXXXX) which is located below the name of your application. ![Pushwoosh Application Code](https://docs.pushwoosh.com/integrations-qonversion-integration-1.webp) * **Pushwoosh API Access Token**. To get the token, go to Settings > API Access and copy the code. ![Pushwoosh API Access Token](https://docs.pushwoosh.com/integrations-qonversion-integration-2.webp) You can learn more about the API Access Token [in this guide](https://docs.pushwoosh.com/developer/api-reference/api-access-token). 2. In the [Integrations section](https://dash.qonversion.io/app/integration/) of your [Qonversion project](https://dash.qonversion.io/site/login), select **Pushwoosh**, and provide the Pushwoosh Application Key and API Access Token. Click **Save**. ![Configuring Pushwoosh integration in Qonversion](https://docs.pushwoosh.com/integrations-qonversion-integration-3.webp) 3. You can either use the default event names provided by Qonversion or customize them to suit your needs. Once you complete these steps, Qonversion will start sending in-app purchase and subscription data to your Pushwoosh account, enabling you to leverage this information for more targeted and effective messaging to your users. --- # RevenueCat integration **URL:** https://docs.pushwoosh.com/product/integrations/revenuecat-integration/ Send RevenueCat subscription and purchase webhooks to Pushwoosh as events for your segments, journeys, and revenue tracking --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [RevenueCat](https://www.revenuecat.com/) is a subscription management platform that simplifies in-app purchases and subscriptions for businesses across iOS, Android, and web platforms. It provides comprehensive tools for building, analyzing, and enhancing in-app subscriptions. By integrating Pushwoosh with [RevenueCat](https://www.revenuecat.com/), businesses can send targeted push notifications, in-app prompts, and other messages to users based on subscription events sent by RevenueCat to Pushwoosh. This can encourage users to renew subscriptions, upgrade to higher tiers, or take advantage of special offers, enhancing engagement, maximizing revenue, and simplifying subscription management. ## Use cases #### Personalized communication Businesses can use the integration to personalize communication with clients. For example, an e-learning platform can boost user engagement by sending personalized push notifications when detecting a subscription plan change. These notifications offer transition guidance, recommend relevant courses based on the new plan, and provide exclusive benefits aligned with the update. #### Billing issue notifications When a user’s subscription faces a billing issue, businesses can use the RevenueCat integration with Pushwoosh to promptly send targeted notifications. For instance, a streaming service can notify the user about the billing problem and provide instructions for resolving it, such as updating payment information. #### Non-renewing purchase notifications Businesses can leverage the RevenueCat integration with Pushwoosh to turn one-time purchases into lasting customer relationships. For example, a meal delivery service can send a post-purchase push notification thanking the customer and recommending relevant products or promoting a subscription plan for continued convenience. This will foster customer loyalty, drive upsell opportunities, and potentially convert one-time buyers into subscribers. #### Tier upgrade promotions Apps with multiple subscription tiers can use the integration to monitor user subscriptions and send tailored upgrade prompts. For instance, a gaming app could use push notifications to underline the benefits of premium levels, like exclusive content or discounts, encouraging users to elevate their subscriptions. ## Events Every webhook RevenueCat sends becomes a Pushwoosh event named `RC_` plus the RevenueCat event type, for example `RC_INITIAL_PURCHASE`, `RC_RENEWAL`, `RC_CANCELLATION`, `RC_BILLING_ISSUE`, or `RC_EXPIRATION`. Pushwoosh creates the event the first time a webhook of that type arrives, so new RevenueCat event types appear in your app on their own. See [Event types](https://www.revenuecat.com/docs/integrations/webhooks/event-types-and-fields) in the RevenueCat documentation for the full list. Each event carries the fields of the RevenueCat webhook as event attributes, for example `price`, `price_in_purchased_currency`, `currency`, `product_id`, `store`, `period_type`, `environment`, `country_code`, and `entitlement_ids`: * Fields that hold a time in milliseconds become dates without the `_ms` suffix, for example `purchased_at` and `expiration_at`. The date the webhook was sent is the `timestamp` attribute. * These dates are in UTC and have the `YYYY-MM-DD` format, without the time of day. * The [customer attributes](https://www.revenuecat.com/docs/customers/customer-attributes) you set in RevenueCat become event attributes too. Attributes that identify a person or a device, such as email, phone number, display name, IP address, advertising IDs, and push tokens, aren’t passed to Pushwoosh. * An event keeps up to 100 attributes. Purchases from the App Store, Google Play, the Mac App Store, and Amazon are recorded on the iOS, Android, macOS, and Amazon platforms. Purchases from other stores, such as Stripe, RevenueCat Billing, or promotional grants, are recorded without a platform. You can use these events in your [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview). Events with the `price` attribute can be used with [RFM segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation). Tip You can also track revenue for events like `RC_INITIAL_PURCHASE`, `RC_RENEWAL`, or `RC_NON_RENEWING_PURCHASE` (using their `price` and `currency` attributes) as [conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/). Avoid tracking revenue for `RC_CANCELLATION` or `RC_EXPIRATION`. Conversion events don’t support refunds or reversals, so they’d stay counted as revenue. ## Setting up the integration The integration works through RevenueCat webhooks, which are available on RevenueCat’s Pro plan. ### Match RevenueCat customers to Pushwoosh users Pushwoosh records a webhook for the Pushwoosh user it names and skips a webhook that names no user at all. Choose how RevenueCat names the user: * If your app identifies users with its own ID and passes it to RevenueCat as the App User ID, use the [App User ID as the Pushwoosh User ID](https://docs.pushwoosh.com/product/integrations/revenuecat-integration/#use-the-revenuecat-app-user-id-as-the-pushwoosh-user-id). * If the App User ID differs from the Pushwoosh User ID, for example when RevenueCat uses anonymous IDs like `$RCAnonymousID:…`, [send the Pushwoosh IDs as RevenueCat attributes](https://docs.pushwoosh.com/product/integrations/revenuecat-integration/#send-pushwoosh-ids-as-revenuecat-attributes). Without them, events go to a user ID Pushwoosh doesn’t have. #### Use the RevenueCat App User ID as the Pushwoosh User ID Set the [App User ID](https://www.revenuecat.com/docs/customers/user-ids) you pass to RevenueCat as the Pushwoosh User ID (`setUserId`). ##### Android Java ```java Pushwoosh.getInstance().setUserId(appUserID); ``` ##### iOS Swift ```swift Pushwoosh.configure.setUserId(appUserID) ``` #### Send Pushwoosh IDs as RevenueCat attributes Set the attributes `pushwooshUserId` and `pushwooshHwid` in RevenueCat. Pushwoosh uses them instead of the App User ID: * `pushwooshUserId`: the Pushwoosh User ID of the customer. * `pushwooshHwid`: the Pushwoosh hardware ID (HWID) of the customer’s device. It’s optional. Without it, Pushwoosh uses the User ID as the HWID. ##### Android Kotlin ```kotlin Purchases.sharedInstance.setAttributes(mapOf( "pushwooshUserId" to pushwooshUserId, "pushwooshHwid" to Pushwoosh.getInstance().hwid )) ``` ##### iOS Swift ```swift Purchases.shared.attribution.setAttributes([ "pushwooshUserId": pushwooshUserId, "pushwooshHwid": Pushwoosh.configure.getHWID() ]) ``` ### Send RevenueCat webhooks to Pushwoosh In your RevenueCat dashboard: 1. Open your project, find the **Integrations** card in the left menu, and click **+ New**. ![Finding Integrations in RevenueCat dashboard](https://docs.pushwoosh.com/integrations-revenuecat-integration-1.webp) 2. Choose **Webhooks**. ![Selecting Webhooks from Integrations menu](https://docs.pushwoosh.com/integrations-revenuecat-integration-2.webp) 3. If RevenueCat shows the list of your webhooks, click **Add new configuration**. 4. Give the integration a straightforward name, such as *Pushwoosh*, for easy identification. 5. Enter the following URL as the webhook URL: `https://integration-revenuecat.svc-nue.pushwoosh.com/integration-revenuecat/v1/post-event?pwapp=APP_CODE` ![Entering Webhook URL for Pushwoosh integration](https://docs.pushwoosh.com/integrations-revenuecat-integration-3.webp) In the URL, replace **APP_CODE** with your existing Pushwoosh application code which is located below the name of your application in your Pushwoosh account. ![Finding Pushwoosh application code](https://docs.pushwoosh.com/integrations-revenuecat-integration-4.webp) 6. In the **Authorization header value** field, insert your **Pushwoosh API token.** You can find this token under **Settings > API Access** in Pushwoosh. ![Entering Pushwoosh API token in authorization header](https://docs.pushwoosh.com/integrations-revenuecat-integration-5.webp) 7. Decide if you want to transmit events for production (live) purchases, sandbox (test) purchases, or both. 8. In the **App** dropdown, specify whether webhook events should be dispatched for a single app or all apps within the project. 9. In the **Event Type** dropdown, select whether to send all events or specify which events to send to Pushwoosh. ![Configuring event types in RevenueCat webhook](https://docs.pushwoosh.com/integrations-revenuecat-integration-6.webp) 10. Choose to show all events, only succeeded events, or only failed events. ![Selecting event display options in webhook](https://docs.pushwoosh.com/integrations-revenuecat-integration-7.webp) To check the connection, send a test webhook from RevenueCat. A successful delivery means Pushwoosh accepted the URL and the API token. The test webhook itself isn’t recorded as an event: each `RC_` event appears under **Audience** → **Events** in your Pushwoosh app once the first real webhook of that type arrives. --- # RudderStack integration **URL:** https://docs.pushwoosh.com/product/integrations/rudderstack-integration/ Send RudderStack track events and identify traits to Pushwoosh as events and user-specific tags for segmentation and journeys --- Developer assistance needed You’ll need help from your development team to set up the integration. Share this guide with them. [RudderStack](https://www.rudderstack.com/) is a customer data platform that collects events from your apps, websites, servers, and warehouses and forwards them to other tools. With the Pushwoosh destination in RudderStack, the data you already collect there reaches Pushwoosh without extra code in your app: * Track events become Pushwoosh [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). Use them to build segments and to start or branch [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview). * Identify traits become user-specific [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). Use them to target and personalize push notifications, in-app messages, and emails. Beta The Pushwoosh destination in RudderStack is in beta. Available on request: contact your Customer Success Manager to get access. Until access is enabled, Pushwoosh doesn’t appear in the RudderStack destination list. The destination works only for Pushwoosh accounts hosted on `app.pushwoosh.com`. If you’re not sure where your account is hosted, contact your Customer Success Manager or support. ## Before you start The destination matches all data by User ID, so check these prerequisites first: * The [Pushwoosh SDK](https://docs.pushwoosh.com/developer/first-steps/integrate-pushwoosh-sdk) is integrated in your app. * Your app calls [`setUserId`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) with the same value that RudderStack sends as `userId`. Pushwoosh links incoming events and traits to users by this value. * Each user’s device is registered with this User ID before RudderStack sends `identify` calls for the user. Traits for a User ID that Pushwoosh doesn’t know yet are dropped. * You have access to **Settings** → **API Access** in Pushwoosh to create an API token. * You have a RudderStack workspace with at least one source that sends `track` or `identify` calls. ## Set up the Pushwoosh destination in RudderStack Setup has two parts: you get two values in Pushwoosh, and then create the destination in RudderStack. ### Get the application code and a Device API token RudderStack needs two values to send data to your application: its application code and a Device API token. 1. In Pushwoosh, open the application you want to send data to. 2. Copy the application code. It’s shown below the application name in the top-left corner and looks like `XXXXX-XXXXX`. 3. Go to **Settings** → **API Access** and click **Generate New Token**. 4. In **Token type**, choose `device`. 5. In **Name**, enter a name for the token, for example `RudderStack`. 6. Click **Generate**. 7. In the token list, click the three-dot menu next to the new token and choose **Edit token…**. 8. Under **Applications**, select the application from step 1 and click **Save**. 9. Copy the token value from the token list. A [Device API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) is enough for the destination. You don’t need a Server API token. ### Add the destination in RudderStack The Pushwoosh destination connects your RudderStack sources to your Pushwoosh application. 1. In the RudderStack dashboard, go to **Collect** → **Destinations**. 2. Click **New destination**. 3. Select **Pushwoosh** from the list. 4. Enter a name for the destination and click **Continue**. 5. Select the sources you want to send to Pushwoosh. 6. In **Application Code**, paste the application code. 7. In **API Token**, paste the Device API token. 8. Finish the setup to save the destination. RudderStack sends the data to Pushwoosh from its own servers (cloud mode), for every source type the destination supports. For the RudderStack side of the setup, see the [Pushwoosh destination guide](https://www.rudderstack.com/docs/destinations/streaming-destinations/pushwoosh/setup-guide/) in the RudderStack documentation. ## What data RudderStack sends to Pushwoosh The destination supports two call types, `track` and `identify`. RudderStack rejects any other call type, such as `page`, `screen`, `group`, or `alias`, for this destination. Every call must have a `userId`. RudderStack doesn’t send calls without it, because Pushwoosh can’t attribute an anonymous call to a user. ### Track calls become Pushwoosh events Each `track` call becomes a Pushwoosh event with the same name as the RudderStack event: | RudderStack field | Pushwoosh | | ------------------------------------------ | ---------------------------------------------------------------------------------------------------- | | `event` | Event name | | `properties` | Event attributes | | `userId` | User ID | | `timestamp` (or `originalTimestamp`) | Time of the event | | `context.device.id` | Device ID, if present | | `context.device.type` or `context.os.name` | Platform: `android`; `ios` for iOS, iPadOS, watchOS, and tvOS; `web` for any other value or no value | If the event or an attribute doesn’t exist in the application yet, Pushwoosh creates it automatically. Pushwoosh drops the whole event in these cases. It returns no error, so RudderStack reports the event as delivered: * The call has more than 100 properties. * A property value is longer than 10 KB. * The event name is longer than 255 characters. * The event is new, and its name is longer than 128 characters or contains control characters. An event keeps up to 100 attributes. Once an event has 100 known attributes, Pushwoosh still records new calls, but new property names aren’t added as attributes. Nested objects in `properties` arrive as JSON strings, and properties with a `null` value are dropped. ### Identify calls become user-specific tags Each `identify` call sets its traits as tags on the user with the given `userId`. Traits come from `traits`, or from `context.traits` when `traits` is empty. * **Only user-specific tags are written.** If [tag auto-creation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#how-to-set-up-a-custom-tag) is enabled for your account, a trait with a new name creates a [user-specific tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#tag-scope-general-vs-user-specific). If a **General** tag with the same name already exists, Pushwoosh skips that trait without an error. * **A value must match the tag type.** If a tag already exists and the trait value doesn’t fit its type, such as text for an Integer tag, Pushwoosh skips that trait without an error. * **The user must already exist in Pushwoosh.** Tags are applied only if the Pushwoosh SDK has registered a device with `setUserId` set to the same `userId`. Traits for an unknown user are dropped without an error. * **Identity and contact traits aren’t copied to tags.** `email`, `phone`, `firstName`, `lastName`, `name`, `address`, `id`, `userId`, and `anonymousId` are never sent as tags. An `identify` call that has only these traits fails with `No traits left to send as Pushwoosh tags`. * **An empty value clears the tag.** A trait with an empty string or an empty list deletes the user’s current value of that tag. * **Nested objects arrive as JSON strings.** Traits with a `null` value are dropped. Older calls can overwrite newer values Each `identify` call overwrites the current tag values, and the last delivered call wins. If RudderStack delivers an older `identify` after a newer one, for example when it retries or replays data, the tags keep the older values. ### What the destination doesn’t do The destination sends only `track` and `identify` data. These tasks stay outside it: * It doesn’t register devices or email addresses. Devices come from the Pushwoosh SDK. * It doesn’t delete users. RudderStack user deletion requests aren’t supported for this destination. ## Check that the integration works After the destination is saved, send a test `track` and `identify` call from a source with a `userId` that already has a device in Pushwoosh. * **Events:** in Pushwoosh, go to **Audience** → **Events**, open the event, and check that it’s counted. * **Tags:** go to **Audience** → **User Explorer**, find the user by User ID, and check the tag values. Delivery errors for each call are shown in the RudderStack **Live Events** view of the destination. ## Troubleshooting Find the error message that RudderStack shows for the destination in **Live Events** to see its cause and fix. ### Track calls fail with unauthorized The API token doesn’t match the application code. Check that the token belongs to the same Pushwoosh account as the application. `identify` calls don’t check the token, so they keep succeeding with a wrong token. Use a `track` call to test the token. ### Calls fail with application not found or application code is not valid The application code is wrong, or the account isn’t hosted on `app.pushwoosh.com`. `track` calls fail with `application not found`, and `identify` calls fail with `application code is not valid`. Check the code in the Pushwoosh Control Panel. If the code is right, contact your Customer Success Manager or support. ### Calls fail with Missing required value from userId The call has no `userId`. Anonymous calls aren’t sent to Pushwoosh. Call `identify` in your RudderStack source after the user signs in, so later calls carry the `userId`. ### Calls fail with Invalid enum value The call type isn’t `track` or `identify`. Pushwoosh accepts only these two types. Either don’t connect sources that send only `page`, `screen`, `group`, or `alias` calls, or ignore these errors. ### Identify calls fail with No traits left to send as Pushwoosh tags All traits in the call are identity or contact traits, such as `email` or `phone`, which are never copied to tags. Add at least one other trait or skip the call. ### Identify calls succeed but tags don’t appear Check these in order: 1. A device with this User ID is registered in Pushwoosh. Search for the user in **Audience** → **User Explorer**. 2. The app calls `setUserId` with exactly the same value RudderStack sends as `userId`. 3. The tag with this name is **User-specific**. A trait for an existing **General** tag is skipped. Change the tag scope or rename the trait. 4. The trait value matches the type of the existing tag. 5. The trait isn’t one of the identity or contact traits that are never copied, such as `email` or `phone`. --- # Segment integration **URL:** https://docs.pushwoosh.com/product/integrations/segment-integration/ Integrate Pushwoosh with Segment to create more relevant targeted omnichannel campaigns --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Segment](https://segment.com/) is a customer data platform that simplifies collecting user data and helps you leverage it effectively. If you store your customer data in Segment, you can set up integration with Pushwoosh in just a few clicks. Once the integration is set, Segment will send the following data to Pushwoosh: * User attributes: name, device, preferences, location, behavior, etc. Pushwoosh will save this data as [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) and their values for each user. * Events: each Segment Event will be transferred to Pushwoosh and saved as a [Pushwoosh Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) with the same name. Pushwoosh will also get information about which events each user has triggered. Using Segment data, you can follow up on your audience with seamless communications in Pushwoosh. Below are two examples of leveraging the power of two platforms: **1. Create targeted campaigns based on user behavior data.** Use Events data from Segment to build precise user segments in Pushwoosh based on specific user behavior. For example, Segment sends data about the *SubscribedToOffers* Event to Pushwoosh. You can build a user segment in Pushwoosh consisting of users who have subscribed to special offers: ![User segment based on SubscribedToOffers event](https://docs.pushwoosh.com/integrations-segment-integration-1.webp) Then, you can create engaging customer journeys with promo messages and discounts for users who subscribed to your offers: ![Customer journey with promo messages for subscribers](https://docs.pushwoosh.com/integrations-segment-integration-2.webp) **2. Personalize messages based on user attributes from Segment.** You can leverage user attributes obtained from Segment to modify the content of your push notifications, in-app messages, and emails. This will allow you to set up relevant and high-converting communications. For example, Segment sends the *searched_product* attribute values to Pushwoosh. You can create a push preset offering a discount on the products from a category a user has previously searched for: ![Push preset with dynamic content for product category](https://docs.pushwoosh.com/integrations-segment-integration-3.webp) Each user will receive a push offering a discount on the exact product category they browsed previously. For example, those who searched for sports shoes will see this message: ![Push notification example for sports shoes category](https://docs.pushwoosh.com/integrations-segment-integration-4.webp) ## Integration overview ### Integration type * **Destination**: Data is sent from Segment to Pushwoosh. * **Source**: Data is sent from Pushwoosh to Segment. ### Prerequisites Paid accounts in both Pushwoosh and Segment ### Synchronized entities **Source** * Push, email, and SMS statistics events **Destination** * Devices * Tags * Events * User deletion ### Use cases **Tracking Notifications** Send push notifications and track their status, including whether they were successfully sent and delivered. **Event Analysis** Analyze user-triggered events, such as push notifications or email deliveries, and ensure these events are accurately recorded in Segment. **User Management** Register and manage user information, including tagging users with relevant attributes or deleting users when needed. ## Setting up integration Note Before setting up integration with Segment, make sure to integrate [Pushwoosh SDK](https://docs.pushwoosh.com/developer/first-steps/integrate-pushwoosh-sdk). ### Configuring a Destination 1. [In the Segment interface](https://app.segment.com/login/), navigate to **Connections** → **Destinations** and click **Add Destination**. 2. Search for **Pushwoosh** and select it. 3. Choose the Sources you want to connect the Destination to. 4. Go to Destination settings: ![Destination settings in Segment](https://docs.pushwoosh.com/integrations-segment-integration-5.webp) 5. Enter the **Pushwoosh API key** and **application code**. Ensure the **Enable Destination** switch is on. After that, click **Save Changes**. Where can I find the API key and application code? You can find the API key in the **Settings** → **API Access** section: ![API Access section in Pushwoosh settings](https://docs.pushwoosh.com/integrations-segment-integration-6.webp) The application code can be found at the top left corner of the Pushwoosh interface, right below the project name: ![Application code in Pushwoosh interface](https://docs.pushwoosh.com/integrations-segment-integration-7.webp) 6. Go to the **Mappings** tab and ensure the **Create or Update User Profile** and **Track Events** options are enabled: ![Mappings tab with Create or Update User Profile option](https://docs.pushwoosh.com/integrations-segment-integration-8.webp) It is important to verify the mapping of event fields from your source to Pushwoosh as the destination to ensure your events are processed correctly. The ***device_platform*** field accepts `ios`, `android`, or `web`. Pushwoosh skips events with any other value. Pushwoosh also rejects an event that has neither ***userId*** nor ***deviceId***. To ensure that your mapping is configured correctly, go to **Pushwoosh as Destination > Settings**. ![Destination settings for mapping configuration](https://docs.pushwoosh.com/integrations-segment-integration-9.webp) Then, select the **Mapping** tab and click **Edit Mapping** for the **Track Events** method. ![Edit Mapping for Track Events method](https://docs.pushwoosh.com/integrations-segment-integration-10.webp) Next, ensure that the mapping of your events to the events sent to Pushwoosh is correct, and that the fields **Device Platform** and **External User ID** (or **Device ID**) are properly matched. ![Event mapping with Device Platform and External User ID](https://docs.pushwoosh.com/integrations-segment-integration-11.webp) Your event may differ from the example provided above. However, the key is to match the data transfer for the platform and device for the event sent to Pushwoosh. If the integration is configured correctly, Pushwoosh will start receiving Events and user attributes from Segment. Based on this data, you can create highly targeted [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) and [personalize](https://docs.pushwoosh.com/product/personalization/dynamic-content/) your push notifications, in-app messages, and emails. ### What Pushwoosh does with data from Segment * **Create or Update User Profile**: Pushwoosh saves user traits as tags and creates missing tags automatically. * With an email in the request, Pushwoosh registers an email device for the user and saves the traits on it. * For a push device, the request needs a device token, a device ID, and a `device_platform` of `ios`, `android`, or `web`. Without them, the traits are not saved on a push device. * Traits named like Pushwoosh default tags, such as Language, Country, or Timezone, are ignored, and so are traits whose value is an object. * **Track Events**: Pushwoosh records each event under its Segment name and creates missing events and attributes automatically. * **User deletion**: Pushwoosh deletes all devices of the user with the given external user ID (`external_id`). The `external_id` field is required. A request without it gets a 400 response and deletes nothing. If Pushwoosh is temporarily unable to process events, it responds with a 503 status so that Segment can retry the delivery. Events that Pushwoosh rejects because of their content, such as an unsupported `device_platform` or a missing user ID and device ID, get a 200 response and are not retried. ### Configuring a Source 1. From your workspace’s [Sources catalog page](https://app.segment.com/goto-my-workspace/sources/catalog), click **Add Source**. 2. Search for “**Pushwoosh**” in the Sources Catalog, select Pushwoosh, and click **Add Source**. 3. On the next screen, give the Source a name and configure any other settings. Note The name is used as a label in the Segment app, and Segment creates a related schema name in your warehouse. The name can be anything, but Pushwoosh recommends using something that reflects the source itself and distinguishes among your environments (For example: Pushwoosh_Prod, Pushwoosh_Staging, Pushwoosh_Dev). 4. Click **Add Source** to save your settings. 5. Copy the **Write key** from the Segment UI. 6. Log in to your Pushwoosh account, and select the Application you want to connect. 7. Navigate to **Settings > 3rd party integration > Segment**, click the **Configure button**, and enter your **Write key** from your Segment workspace. 8. In **Events**, select the events you want to export, then click **Apply**. Pushwoosh exports only the selected events, so the integration can’t be saved without at least one. #### Stream Pushwoosh uses the Segment stream Source component to send Segment event data. It uses a server-side track method to send data to Segment. These events are then available in any destination that accepts server-side events, and are available in a schema in your data warehouse. By default, Pushwoosh passes the Pushwoosh User ID as the Segment userId property. If there is no associated User ID, Pushwoosh will pass the HWID as the Segment anonymousId. #### Events The table below lists events that Pushwoosh sends to Segment. These events appear as tables in your warehouse, and as regular events in other Destinations. | Event Name | Description | | ------------------ | ----------------------------------------------- | | Email Sent | Email was sent successfully | | Push Sent | Push notification successfully sent | | SMS Sent | SMS sent to recipient | | Push Opened | Push notification was opened | | Email Opened | Email notification was opened | | Email Delivered | Email successfully delivered | | Push Delivered | Push notification successfully delivered | | SMS Delivered | SMS successfully delivered | | SMS Not Delivered | SMS delivery failed | | Email Bounced | Email returned to sender due to permanent error | | Email Unsubscribed | Email unsubscribed link clicked | #### Event Properties The table below list the properties included in the events listed above. | Property Name | Description | | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | | `message_type` | Channel of the message: `push`, `email`, or `sms`. Omitted if the channel is unknown | | `campaign_code` | Code of the campaign the message belongs to. Present only for messages sent from a campaign | | `device_type` | Pushwoosh platform code of the device, a number. See the `device_type` values in [/registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) | #### Adding Destinations Now that your Source is set up, you can connect it with Destinations. Log into your downstream tools and check to see that your events appear as expected and that they contain all of the properties you expect. If your events and properties don’t appear, check the Event Delivery, and refer to the Destination docs for each tool for troubleshooting. If there are any issues with how the events are arriving to Segment, [contact our support team](mailto:support@pushwoosh.com). ## How the integration works 1. **Push notification initiation**: A client starts the process by initiating a push notification through the Pushwoosh interface. 2. **Event production**: The Pushwoosh backend processes the push notification request and generates an event. This event is then sent to a Kafka topic named `channels-tracking-log` managed by Pushwoosh. 3. **Event consumption**: The Segment Integration service continuously listens to the `channels-tracking-log` Kafka topic and consumes the events as they arrive. 4. **Event processing**: The consumed events are processed by the Segment Integration service, which transforms them into Segment Track Events. 5. **Event transmission**: The processed Segment Track Events are then sent to the Segment backend for further handling and analysis. ## How do you know that the integration is working? #### Destination ##### track-user Go to your **Pushwoosh account > Your Connected Application > Audience > User Explorer.** Find your user/device and check tags which Segment sent. ##### post-event Go to your **Pushwoosh account > Your Connected Application > Audience > Events**. Select the event, click the three-dot menu, and choose Event Statistics. Make sure that the event is counted. ##### delete-user Go to your **PW account > Your Connected Application > Audience > User Explorer.** Make sure that user/device does not exist. #### Source Use [Source debugger](https://segment.com/docs/connections/sources/debugger/) to detect events which were sent from Pushwoosh to Segment. ## Troubleshooting ### Check integration status To troubleshoot issues with the Pushwoosh integration, start by verifying the following: **Destination** * **Track-User**: Verify user/device information and tags in Pushwoosh under `Audience > User Explorer`. * **Post-Event**: Ensure the event is counted in Pushwoosh by checking event statistics. * **Delete-User**: Confirm that the user/device no longer exists in Pushwoosh. **Source** Use the source debugger at [Segment Debugger](https://segment.com/docs/connections/sources/debugger/) to check if events from Pushwoosh are properly sent to Segment. ### Users are not deleted Check that the delete request includes the `external_id` field with the user ID. Without it, Pushwoosh responds with a 400 status and deletes nothing. ### Event mismatch If events are not appearing correctly in Segment, ensure that Pushwoosh events are correctly mapped to Segment Track Events as per the provided mapping rules. ### Authentication issues Verify that the correct authentication tokens or API keys are used in the requests to Pushwoosh and Segment. ### Data flow issues Confirm that the data flow between Pushwoosh and Segment is uninterrupted and that Kafka topics are being monitored correctly. ## FAQ ### What types of events are sent to Segment from Pushwoosh? Pushwoosh sends the events you select in the integration settings: **Push Sent, Push Delivered, Push Opened, Email Sent, Email Delivered, Email Opened, Email Bounced, Email Unsubscribed, SMS Sent, SMS Delivered, and SMS Not Delivered**. ### How can I verify that events are correctly sent to Segment? Use the Segment source debugger to check for events sent from Pushwoosh, and review event statistics in your Pushwoosh account to ensure they are properly counted. ### What should I do if I encounter issues with the integration? Check the integration status by verifying user/device information, event counts, and user deletions in Pushwoosh. Additionally, ensure that the correct authentication tokens are used and that the data flow is uninterrupted. ### How is user and device information handled in the integration? Pushwoosh registers or updates the device and saves user traits as tags. Missing tags are created automatically. Traits named like Pushwoosh default tags, such as Language or Country, are ignored. ### Where can I find detailed documentation for the integration? Detailed documentation is available in the Segment and Pushwoosh integration manuals, as well as in their respective API documentation. --- # Set up Shopify **URL:** https://docs.pushwoosh.com/product/integrations/shopify/ Install the Pushwoosh Shopify app, connect your store, set up User IDs, extensions, and web push opt-in on the storefront. --- ## Integration overview Pushwoosh integrates seamlessly with Shopify, a leading e-commerce platform that enables businesses to create, manage, and scale online stores. This guide walks you through installing and configuring the Pushwoosh app in your Shopify store, setting up user identification, and using notifications and automation tools to engage customers across channels like push, email, SMS, and WhatsApp. ### Integration type * **Destination**: Shopify sends customer data to Pushwoosh, where it is used for targeting and messaging. ### Prerequisites * A [Pushwoosh account](https://www.pushwoosh.com/) * Access to your Shopify admin panel * Pushwoosh [Device API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token) and **Application Code** * A Shopify store with customers and order data available * Approval of the `read_orders` access scope for order/cart events, and `write_pixels` plus `read_customer_events` for checkout step tracking Already connected? You'll be asked to re-approve access If you connected the Pushwoosh app before order, cart, and checkout tracking existed, Shopify asks you to approve the new scopes the next time you open the app — this is the expected way Shopify rolls out added permissions, not an error. Approve them to start receiving order, cart, and checkout events. ### Synchronized entities * **Customers**: Used to assign and identify Pushwoosh User IDs via Shopify metafields Note ##### Customer synchronization approaches in Pushwoosh Due to technical limitations, Pushwoosh offers several customer synchronization methods, depending on the communication channel used for notifications: * **Email, Web, SMS & WhatsApp**: Devices/users are automatically registered in the Pushwoosh application. * **Mobile & Desktop Apps**: Devices/users can be synchronized manually via the Pushwoosh and Shopify admin panels. * **Web Platforms (Chrome, Firefox, Safari)**: Devices/users are managed by the Shopify theme embed itself. ##### How does it work? The synchronization process operates as follows: 1. The system attempts to retrieve the current user session to obtain an email or phone number (identifiers that function without additional configuration). 2. If successful, the email or phone number is set as the `userID` identifier for both Pushwoosh and the `pushwoosh_userid` property in the customer data. 3. If this is not possible, the default Pushwoosh SDK `userID` is assigned as the identifier for both Pushwoosh and the `pushwoosh_userid` property. 4. In any case, a `userID` will be assigned on both sides, ensuring consistent synchronization. * **Customer segments**: Used for group-based targeting and messaging * **Events and triggers** (e.g., orders placed, tags applied): Used in Shopify Flow to automate messaging across channels * **Storefront**: Used for capturing web push opt-ins through embedded scripts * **Product feed**: Your store’s storefront feed (`products.json`) connects automatically to the Pushwoosh [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) when you connect the app, and disconnects automatically if you uninstall it. * **Orders and carts**: Server-to-server events from Shopify’s `orders/paid`, `checkouts/create`, and `checkouts/update` webhooks. See [Shopify events and campaigns](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/). * **Storefront browsing behavior**: Product views and add-to-cart actions, collected by the same theme embed used for web push opt-ins. * **Checkout steps**: Contact info entered and payment info submitted, collected via a Shopify Web Pixel. ### Glossary (mapping entity names if they are different) | **Shopify** | **Pushwoosh** | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Customer | User ID | | `orders/paid` webhook | Event `PW_OrderCreated` — shared with other e-commerce integrations (e.g. [Magento](https://docs.pushwoosh.com/product/integrations/magento-integration/#sync-selected-orders-to-pushwoosh)) | | `checkouts/create` webhook | Event `PW_AbandonedCart` — same shared event, also used by the Products block’s [Cart source](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart) | | `checkouts/update` webhook | Event `PW_AbandonedCartUpdate` — fires as the shopper keeps editing the same checkout, overwriting the stored cart from `PW_AbandonedCart` | | Product page view | Event `PW_ProductViewed` | | Add to cart (no page reload) | Event `PW_AddToCart` | | Web Pixel `checkout_contact_info_submitted` | Event `PW_CheckoutContactInfo` | | Web Pixel `payment_info_submitted` | Event `PW_PaymentInfoSubmitted` | ## How the integration works Pushwoosh acts as a messaging destination for Shopify. Once integrated, Shopify sends customer and behavioral data to Pushwoosh, where it powers real-time notifications, customer segmentation, and personalized messaging across channels. ### Key components of the integration 1. **Customer identification via metafields** Each Shopify customer is assigned a unique User ID, stored in a metafield (`custom.pushwoosh_userid`). This ID allows Pushwoosh to recognize and message users consistently across platforms. 2. **Event-driven automation via Shopify Flow** Pushwoosh provides Flow actions that allow you to automate messaging (push, email, SMS, WhatsApp) based on Shopify events, such as: * New order placed * Tag applied to customer * Cart abandonment 3. **Segment-based targeting** Customer segments from Shopify can be used to send group-specific notifications across channels, such as promotions for first-time buyers or reactivation campaigns for dormant users. 4. **Manual and real-time messaging** From the Shopify customer or segment view, you can manually trigger notifications using the Pushwoosh Extensions: * **Notification by Customer**: For personal follow-ups or testing * **Notification by Segment**: For campaign-based messages ### Web push notifications via storefront embed By enabling the Pushwoosh embed in your Shopify theme, you can prompt website visitors to opt in to browser-based push notifications—no mobile app required. #### Data flow overview ```plaintext Shopify Customer & Event Data ↓ Pushwoosh API (via App) ↓ User ID → Segmentation → Triggered or Manual Notifications ``` This integration empowers you to deliver timely, relevant messages that improve user retention, boost conversions, and enhance the customer experience—automatically or on demand. ## Part 1. Installation ### Log into Shopify 1. Go to [Shopify Admin](https://admin.shopify.com/) 2. Select your store from the dashboard Note This guide assumes that you already have a Pushwoosh account. If not, [create one](https://www.pushwoosh.com/) ### Install the Pushwoosh app 1. Visit the [Pushwoosh App Store page](https://apps.shopify.com/pushwoosh-notifications) 2. Click **Install** ![Install the Pushwoosh app](https://docs.pushwoosh.com/integrations-shopify-integration-19.webp) 3. Confirm installation in the Shopify dialog ![Confirm installation](https://docs.pushwoosh.com/integrations-shopify-integration-1.webp) ### Connect your Pushwoosh app After installation, you will be redirected to the Pushwoosh app inside Shopify. 1. Click **Connect**. ![Click Connect button](https://docs.pushwoosh.com/integrations-shopify-integration-22.webp) 2. Enter the **Application Code** and **API Key**, which you can find in the **Pushwoosh Control Panel → Settings → API Access**. The API Key here is your [Device API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). ![Connect your Pushwoosh app](https://docs.pushwoosh.com/integrations-shopify-integration-2.webp) When connected, a success message will appear: “Pushwoosh app connected.” ![Pushwoosh app connected](https://docs.pushwoosh.com/integrations-shopify-integration-3.webp) When Shopify asks for permissions (orders, pixels, customer events), approve them. Those scopes are required for [Shopify events and campaigns](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/) tracking. If you connected the app earlier, see [Already connected? You’ll be asked to re-approve access](https://docs.pushwoosh.com/product/integrations/shopify/#prerequisites). Automatic product catalog connection Connecting the app also connects your store’s storefront feed to the Pushwoosh [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) automatically. No separate feed setup needed. Your store’s currency is captured at the same time, and updates automatically if you change your store’s currency afterward. ### Subscribe to a plan 1. Click **Upgrade Subscription** 2. A single usage-based plan will be shown. 3. Confirm the billing. ![Subscribe to a plan](https://docs.pushwoosh.com/integrations-shopify-integration-4.webp) Note Billing is required to use message delivery and segmentation features. ## Part 2. Setup: Customer User ID via metafields To deliver notifications to identified users across channels (push, email, SMS, WhatsApp), Pushwoosh uses a unique **User ID** per customer. Shopify stores this value inside Shopify’s **Customer metafields**. 1. Go to **Settings → Metafields and metaobjects → Customers** ![Add definition](https://docs.pushwoosh.com/integrations-shopify-integration-6.webp) 2. Click **Add definition** and use these values: | Field | Value | | ----------------------- | ------------------------ | | Namespace and key | custom.pushwoosh_userid | | Namespace | custom | | Type | Single line text | | Validation | None | | Customer Account Access | Read and Write | | Storefront API Access | Enabled | ![Add definition](https://docs.pushwoosh.com/integrations-shopify-integration-7.webp) Set **Customer accounts access** and **Storefront API access** as in the table above. ![Add definition](https://docs.pushwoosh.com/integrations-shopify-integration-9.webp) Why this matters This metafield is a persistent user identifier across mobile/web devices for notifications and segmentation. ## Part 3. Using Pushwoosh extensions Pushwoosh includes Shopify Extensions for segment targeting, automation (Flow), and customer-specific notifications. ### Extension 1. Send notification by customer #### Use case Manually send personalized messages or test notifications to a specific user. #### Steps 1. Open **Customers → Select a customer** 2. Click **More actions → Notification by customer**\ ![Notification by customer](https://docs.pushwoosh.com/integrations-shopify-integration-10.webp) 3. Select the messaging platform (e.g., Email) and a [content preset](https://docs.pushwoosh.com/product/content/push-presets/). ![Fill the form](https://docs.pushwoosh.com/integrations-shopify-integration-11.webp) 4. Click **Send** Tip Use this for post-purchase reminders, personal thank-yous, or reactivation prompts. ### Extension 2. Notification by segment #### Use case Target groups of customers—abandoned carts, first-time buyers, high spenders. #### Steps 1. Go to **Customers → Segments** 2. Choose a segment → Click **Use Segment** ![Use Segment](https://docs.pushwoosh.com/integrations-shopify-integration-12.webp) 3. Select **Notification by Segment** ![Notification by Segment](https://docs.pushwoosh.com/integrations-shopify-integration-13.webp) 4. Select the desired communication channel and message preset. Click **Send**. ![Fill out the message form](https://docs.pushwoosh.com/integrations-shopify-integration-14.webp) Tip Segments can be created based on purchase history, location, activity, or tags. ### Extension 3. Pushwoosh in Shopify Flow #### Use case Automate messaging in customer journeys using triggers like orders, tags, or behavior. #### Steps 1. Create a new Flow in **Shopify Flow** 2. Add action → Choose **Pushwoosh send notification** ![Pushwoosh flow action](https://docs.pushwoosh.com/integrations-shopify-integration-15.webp) 3. Configure message and dynamic fields ![Configure message and dynamic fields](https://docs.pushwoosh.com/integrations-shopify-integration-16.webp) For example: * Send a message after order placed * Remind user when cart is abandoned * Send reactivation push after 30 days of inactivity ## Part 4. Enable web push (and storefront browsing events) Optional for web push alone. Required if you need product-view and add-to-cart events on [Shopify events and campaigns](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/). Turn on the theme embed for browser web push on your storefront: 1. Go to **Online Store → Themes → Customize**\ ![Go to Themes → Customize](https://docs.pushwoosh.com/integrations-shopify-integration-17.webp) 2. Open **App Embeds** from the left menu 3. Toggle **Push Init Embed** ON ![Toggle Push Init Embed ON](https://docs.pushwoosh.com/integrations-shopify-integration-18.webp) Note Now your store can prompt users to opt-in to web push directly from the storefront. If you skip this toggle, you also won’t get storefront browsing events on [Shopify events and campaigns](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/). Order paid and checkout events still arrive without it. ## Data compliance Shopify requires every public app to respond to its mandatory GDPR/CCPA webhooks. Pushwoosh handles them as follows: * **`customers/redact`**: When a shopper asks the store to erase their data, Pushwoosh erases the matching Pushwoosh records by whichever identifiers Shopify sends — email, phone, and Shopify customer ID. * **`customers/data_request`**: When a shopper requests a copy of their data, Pushwoosh posts a notification to support’s internal channel, and a person handles the request manually. Shopify requires apps to respond to these within 30 days. * **`shop/redact`**: Shopify sends this 48 hours after a store uninstalls the app, asking to erase the store’s data. Pushwoosh re-runs the same deletion the uninstall itself triggers, clearing any session left over and erasing the store’s integration record (device API key, application code, and billing IDs included). This exists as a safety net for the case where uninstall didn’t fully clean up the first time. ## Use cases summary After setup, common ways to message from Shopify: | Scenario | Tool used | | -------------------------------- | ------------------------------- | | Personalized follow-up | Notification by Customer | | Segment-based promotion | Notification by Segment | | Welcome message after signing up | Flow + Metafield tracking | | Cart abandonment reminder | Shopify Flow + Pushwoosh action | | Push opt-in | Theme Embed + Push Init Toggle | For journeys and segments on order, cart, checkout, and storefront events, see [Shopify events and campaigns](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/). --- # Shopify events and campaigns **URL:** https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/ Track Shopify orders, carts, checkout steps, and storefront browsing events, then use them in journeys, segments, and campaigns. --- After you connect the app and approve Shopify’s requested permissions for order, cart, and checkout tracking (`read_orders`, `write_pixels`, `read_customer_events`), order, checkout, and checkout-step events start arriving without a separate Pushwoosh tracking screen. See [Prerequisites](https://docs.pushwoosh.com/product/integrations/shopify/#prerequisites) and [Part 1](https://docs.pushwoosh.com/product/integrations/shopify/#part-1-installation) for the approval step. Storefront browsing (product views and add-to-cart) also needs [**Push Init Embed** ON](https://docs.pushwoosh.com/product/integrations/shopify/#part-4-enable-web-push-and-storefront-browsing-events). Tracking covers three independent event groups. Each needs different access, so turning one off does not pause the others: * **Order paid and checkout:** arrive from Shopify even if the [theme embed](https://docs.pushwoosh.com/product/integrations/shopify/#part-4-enable-web-push-and-storefront-browsing-events) is off * **Storefront browsing** (product views and add-to-cart): need the theme embed **ON** * **Checkout steps:** need Shopify’s checkout-tracking permissions from [Part 1](https://docs.pushwoosh.com/product/integrations/shopify/#part-1-installation) (`write_pixels` and `read_customer_events`), approved separately from the order-tracking permission (`read_orders`) above Before you connect the app, check [how Pushwoosh handles money and date attribute types](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/#order-paid-and-checkout-events) for `PW_OrderCreated` and `PW_AbandonedCart`. A wrong type sent before you catch it can’t be fixed retroactively. ## Order paid and checkout events Use these for post-purchase messaging (`PW_OrderCreated`), abandoned-checkout journeys or segments (`PW_AbandonedCart` / `PW_AbandonedCartUpdate`), and purchase conversion mapping. They arrive from Shopify even if the shopper blocks browser tracking: Requires the app connected and read_orders approved No extra toggle beyond that: connect the app in [Part 1](https://docs.pushwoosh.com/product/integrations/shopify/#part-1-installation) and approve Shopify’s order-tracking permission (`read_orders`). Unlike storefront browsing, these events don’t depend on the theme embed being on. | Event name in Pushwoosh | When it fires | | ------------------------ | ----------------------------------------------------------------------- | | `PW_OrderCreated` | An order is paid | | `PW_AbandonedCart` | A shopper starts checkout | | `PW_AbandonedCartUpdate` | The shopper adds, removes, or changes items during an existing checkout | These correspond to the order and checkout notifications Shopify sends to Pushwoosh automatically (the `orders/paid`, `checkouts/create`, and `checkouts/update` webhooks, listed under [Synchronized entities](https://docs.pushwoosh.com/product/integrations/shopify/#synchronized-entities)). Shopify’s own Flow condition, the `checkouts/create` webhook, and the Products block’s Cart source are three different things that all get called **cart abandonment**. Pick the one that matches what you’re building, using the table below. | What you want to build | What to use | | ------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Reminder from Shopify Flow (Shopify’s own **Cart abandonment** condition) | Shopify Flow + **Pushwoosh send notification** action. Not the `PW_AbandonedCart` event names. | | Reminder or segment in Pushwoosh from checkout started or cart changed | Customer Journey or segment on `PW_AbandonedCart` / `PW_AbandonedCartUpdate` | | Show the shopper’s cart items inside an email | Products block [Cart source](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart) (uses the `PW_AbandonedCartID` tag at send time). Separate from which trigger started the journey. | **How these events behave.** `checkouts/create` only means a checkout was started. Shopify hasn’t decided it’s abandoned, and no order has been confirmed either. While the shopper edits checkout, `PW_AbandonedCartUpdate` fires again for the same checkout and overwrites the cart stored from `PW_AbandonedCart`. Reminder emails that use Cart source pick up the latest items at send time. You don’t need a second journey trigger only to refresh the cart. `PW_AbandonedCart` and `PW_AbandonedCartUpdate` set the `PW_AbandonedCartID` tag on the shopper, which is what Cart source reads at send time. `PW_OrderCreated` clears it right away, so a shopper who already checked out skips the block instead of getting a reminder for a cart they no longer have. See [What happens when you send PW_OrderCreated](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#what-happens-when-you-send-pw_ordercreated) for the exact clearing behavior. A shopper who starts checkout twice re-enters the journey only if you allow it By default, a Customer Journey doesn’t re-enter someone who’s already in it, so a second `checkouts/create` from the same shopper while their first abandoned-cart journey is still running is ignored, not restarted. Turn on **Allow re-entry** on the trigger if you want each new checkout to reset their reminder timing instead. See [Control whether users can re-enter the campaign](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-whether-users-can-re-enter-the-campaign). ### Attributes of order and checkout events `PW_OrderCreated` and `PW_AbandonedCart` include: | Attribute | Description | | ---------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `orderId` | The order or checkout identifier | | `email` | The shopper’s email | | `totalPrice` | The order or cart total | | `shippingAmount` | The shipping cost | | `taxAmount` | The tax amount | | `discountAmount` | The discount amount | | `currency` | The currency code | | `items` | The cart’s line items | | `customerId` | Only for registered customers, not present for guest checkouts | | `orderUrl` | The order status page for `PW_OrderCreated`, or the abandoned checkout’s recovery URL for `PW_AbandonedCart` and `PW_AbandonedCartUpdate`. Use it to build a “Complete your purchase” link or button. | | `createdDate` | When the order or checkout was created | | `abandonedDate` | `PW_AbandonedCart` and `PW_AbandonedCartUpdate` only. When the cart was last touched, not when it was first created | Get the money and date attribute types right Pushwoosh infers each attribute’s type from the first event it sees, and that inference is unreliable for money and date fields like `totalPrice`, `shippingAmount`, `taxAmount`, `discountAmount`, and `createdDate`/`abandonedDate` above. A whole-number price like `129` infers as an integer, which then truncates `129.90` to `129` on every later event. **Haven’t connected the app yet?** Declare `PW_AbandonedCart` and `PW_OrderCreated` with the correct types before you do, using the table in [Create the events in your Control Panel](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#create-the-events-in-your-control-panel). Skip that guide’s **Send the events from your backend** step. The Shopify integration already sends them for you. **Already connected?** Check the types now: go to **Audience → Events** in the Control Panel, open `PW_AbandonedCart` and `PW_OrderCreated`, and compare each attribute against the same table. Fixing a wrong type there corrects it going forward, but any value already sent under the wrong type is gone for good. Guest checkouts can still send order and cart events, but without a registered customer ID on the event. `PW_AbandonedCartUpdate` carries the same attributes. If your account also has the [Magento](https://docs.pushwoosh.com/product/integrations/magento-integration/) integration connected, its orders and abandoned carts post to these same event names, so a segment or journey built on `PW_OrderCreated` / `PW_AbandonedCart` catches both stores, and the attribute types above apply to both too. Magento has no equivalent to `PW_AbandonedCartUpdate`, because it doesn’t track in-progress checkout edits. `items` in particular arrives from Shopify with only a few fields filled in. Cart items are enriched from your Product Catalog The `items` Shopify sends on `PW_AbandonedCart` and `PW_AbandonedCartUpdate` carry only `productId`, `name`, `sku`, `quantity`, and `price`. Pushwoosh fills in whatever else that item is still missing, such as `description`, `imageUrl`, `productUrl`, `category`, and other catalog fields, from the matching product in your connected [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) (see [Automatic product catalog connection](https://docs.pushwoosh.com/product/integrations/shopify/#connect-your-pushwoosh-app)) whenever a `productId` matches. There’s nothing extra to send for this. A product missing from the catalog just keeps showing with the fields Shopify sent. ## Storefront browsing events Use these for browse-abandonment segments or journeys, and for email recommendation strategies that need product views (for example **Based on what they viewed**). See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/). Requires Push Init Embed ON Turn **Push Init Embed** **ON** in [Part 4](https://docs.pushwoosh.com/product/integrations/shopify/#part-4-enable-web-push-and-storefront-browsing-events). Same theme embed as web push. Nothing else to add to the theme. If the embed is off, or a shopper blocks its script, you lose product views and add-to-cart only. `PW_OrderCreated` and `PW_AbandonedCart` still arrive from Shopify. Both events are collected by the same theme embed. Nothing else to add to the theme once **Push Init Embed** is **ON**: * `PW_ProductViewed`: the shopper opens a product page. * `PW_AddToCart`: the shopper adds an item to cart without reloading the page, which is how most Shopify themes do it. Two things to check before you build on these events * `PW_ProductViewed` counts every page load, with no filtering. Reloads, repeat views in the same visit, and even bot traffic all count the same as a real product view. A recommendation strategy built on this event inherits that noise. * `PW_AddToCart` only fires if your store’s **Add to cart** button doesn’t reload the page. Test this yourself: click **Add to cart** in your store and watch what happens. Does just the cart counter update, or does the whole page refresh? Most Shopify themes do the first, updating the counter without reloading (a background request to `/cart/add`). If your theme reloads the page instead, `PW_AddToCart` will never fire, but `PW_ProductViewed` and the order/checkout events still work fine either way. ### Attributes of storefront browsing events Both events include: | Attribute | Description | | ------------ | ----------------------------------------------------------------------------------------- | | `items` | The viewed or added product(s), each with `productId`, `name`, `sku`, `quantity`, `price` | | `totalPrice` | The combined price of `items` | | `productUrl` | The page the shopper was on | | `currency` | Only when the storefront exposes it | A message that shows `totalPrice` needs a fallback for when `currency` is missing, since the number alone doesn’t say which currency it’s in. Neither event carries `email` or `customerId` as an attribute. The shopper is identified the same way the rest of the theme embed does it, through the SDK session tied to the metafield User ID (see [Part 2](https://docs.pushwoosh.com/product/integrations/shopify/#part-2-setup-customer-user-id-via-metafields)), not through the event payload. `PW_ProductViewed` and `PW_AddToCart`’s `items` carry the same fields Shopify sends before enrichment: `productId`, `name`, `sku`, `quantity`, `price`. Pushwoosh doesn’t fill in the rest of the catalog fields for these two events, the way it does for `PW_AbandonedCart`’s `items` (see [Cart items are enriched from your Product Catalog](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/#order-paid-and-checkout-events) above). A recommendation strategy that needs full product details, like **Based on what they viewed**, looks them up from your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) by `productId` when the message renders. See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/) for what each strategy needs. ## Checkout step events Use these to see where people drop off before purchase, and to message people who started checkout but did not reach a paid order. Requires pixel-related app permissions Approve the checkout-tracking permissions Shopify asks for when you connect the app (`write_pixels` and `read_customer_events`). See [Part 1](https://docs.pushwoosh.com/product/integrations/shopify/#part-1-installation) for that step. No theme embed step. Shopify reports these milestones automatically once those permissions are approved (technically, via Shopify’s [Web Pixel](https://shopify.dev/docs/api/web-pixels-api)). * `PW_CheckoutContactInfo`: the shopper entered their contact information. * `PW_PaymentInfoSubmitted`: the shopper submitted their payment details. A completed sale is `PW_OrderCreated` from the section above, not these two events. `PW_AbandonedCart` only tells you a checkout started. These two events mark two later points in the same checkout, in that order: contact info first, payment details second. A shopper who reached `PW_PaymentInfoSubmitted` and never got a following `PW_OrderCreated` got further than one who only triggered `PW_AbandonedCart`, so a message built on that stage can address a stalled payment specifically, instead of a generic cart reminder. ### Attributes of checkout step events Both events include: | Attribute | Description | | ------------ | ----------------------------------------------------------------------------------------------------- | | `items` | The checkout’s line items, each with `productId`, `name`, `sku`, `quantity`, `price` | | `orderId` | The checkout token, or the Shopify order ID once the checkout has one | | `totalPrice` | The checkout total | | `email` | Only when the checkout has one. A phone-based checkout (no email entered) sends the event without it. | | `currency` | Only when the checkout has one | ## Check that it works Do the matching action in the store, then confirm the event name shows up in Pushwoosh. The event itself is processed in real time, but the two places you’d check it react differently to a name Pushwoosh hasn’t seen before: * In the **Event** dropdown when you [create a segment by events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/), a new name appears right away. * In the **Event** dropdown when you add a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) trigger, a new name can take up to 10 minutes to show up (that dropdown is cached). If you don’t see it yet, wait rather than assume the integration is broken. | Test action | Event | | -------------------------------------------- | ------------------------- | | Pay a test order | `PW_OrderCreated` | | Start a checkout | `PW_AbandonedCart` | | Change items in that checkout | `PW_AbandonedCartUpdate` | | Open a product page (**Push Init Embed** ON) | `PW_ProductViewed` | | Add an item to cart | `PW_AddToCart` | | Enter contact info at checkout | `PW_CheckoutContactInfo` | | Submit payment info at checkout | `PW_PaymentInfoSubmitted` | ## Use the events After you know which event you need, use it in two places: * As a trigger or segment condition: pick it in the **Event** dropdown when you [create a segment by events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) or add a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) trigger. * In the message text: on the message step, turn on **Personalize message with event attributes** and pick the attribute (for example the order total from `PW_OrderCreated`). You can also insert the attribute with a Liquid placeholder. See [Dynamic Content and Liquid Templates in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/). For paid orders, you can also track revenue for `PW_OrderCreated` as a [conversion event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) so Shopify purchases feed RFM segmentation, journey attribution, and [Product block statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/). ### Example campaigns Each one fires at a different point in the shopper’s path, so they reach different audiences: how far someone got before dropping off, or whether they reached checkout at all, decides which one applies. * **Abandoned-cart reminder.** Catches every shopper who started checkout, no matter how far they got: the widest audience here. Build it as a Customer Journey: trigger on `PW_AbandonedCart`, wait for `PW_OrderCreated`, then send an email with a [Cart source](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart) block to whoever hasn’t paid. See [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/) for the full setup. * **Follow-up for stalled checkouts.** Catches only shoppers who got as far as entering contact info but never paid, a narrower slice of the audience above. Segment on `PW_CheckoutContactInfo`, then exclude anyone who also has `PW_PaymentInfoSubmitted` or `PW_OrderCreated`. * **Recovery for a stalled payment.** Catches shoppers who reached payment but never completed the order, further along than the follow-up above and mutually exclusive with it. Segment on `PW_PaymentInfoSubmitted`, then exclude anyone who also has `PW_OrderCreated`. * **Browse recommendation email.** Catches shoppers who looked at products but never started checkout at all, a different audience from the reminders above. Use `PW_ProductViewed` with the Products block’s **Based on what they viewed** strategy. See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/). --- # Singular integration **URL:** https://docs.pushwoosh.com/product/integrations/singular-integration/ Integrate Pushwoosh with Singular for push attribution and postbacks that send attribution data for segmentation and analytics. --- Developer assistance needed You will need help from your development team to set up the integration. Share this guide with them. ## Integration overview [Singular](https://www.singular.net/) is a marketing analytics and attribution platform. Connect it with Pushwoosh to attribute app opens from your push campaigns and measure push performance in Singular alongside other marketing channels. You can also send attribution data from Singular to Pushwoosh through postbacks and use it for segmentation and messaging. ### Use cases * **Push notification attribution:** Attribute app opens from Pushwoosh push campaigns in Singular when users tap your notifications. * **Campaign performance analysis:** Compare push campaign results with other marketing channels in the Singular analytics dashboard. * **ROI optimization:** Identify your best-performing push campaigns in Singular and apply those insights when planning new campaigns in Pushwoosh. * **Segmentation in Pushwoosh:** Use install and in-app event data from Singular postbacks to build segments and personalize campaigns in Pushwoosh. ### Prerequisites * Active Pushwoosh account * Active Singular account * Singular SDK integrated into your app * [Application Code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) and [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#api-access-token) from your Pushwoosh account Minimum Singular SDK versions * Android: Singular Android SDK 12.11.0 or newer (older versions do not support push payload attribution) * iOS: Singular iOS SDK 12.0.5 or newer ### Integration type * **Source (Pushwoosh → Singular):** Attribution data is passed from push payloads on the device into Singular via the Singular tracking link. * **Destination (Singular → Pushwoosh):** Singular sends attribution data to Pushwoosh through postbacks. Pushwoosh stores it as tags and events for segmentation and analytics. ### How it works ##### Push attribution (Pushwoosh → Singular) 1. Pushwoosh delivers a push with the Singular tracking link as a deep link. 2. The user taps the notification. 3. The app passes the link to the Singular SDK. 4. Singular records the open as attributed to your push campaign. ##### Postbacks (Singular → Pushwoosh) 1. A user installs the app, re-engages from a tracked link, or triggers an in-app event that Singular attributes. 2. Singular sends a postback to Pushwoosh with the attribution data. 3. Pushwoosh stores the data as tags and events on the user profile. 4. You use this data for segmentation, analytics, and campaigns in Pushwoosh. ## Configure the link for your application Start the Pushwoosh and Singular integration by creating a partner link in Singular. This step is required whether you use postbacks, push attribution, or both. 1. In your Singular account, go to **Attribution Setup** → **Manage Links**. ![Singular Attribution Setup Manage Links page for creating a partner tracking link](https://docs.pushwoosh.com/integrations-singular-integration-2.webp) 2. Choose **Link Type** → **Partner** and select **Pushwoosh** as the **Source Name**. Enter a **Tracking Link Name** so you can identify this link in Singular analytics. It appears in reports as **Tracker Name**. ![Singular partner link form with Pushwoosh source, Tracking Link Name field, and Generate button](https://docs.pushwoosh.com/integrations-singular-integration-3.webp) 3. In **Link Settings and Redirects**, define which app this link is for and where users go when they tap it. [Learn more](https://support.singular.net/hc/en-us/articles/13000445013531-How-to-Build-Tracking-Links). 4. In **Attribution Settings**, configure attribution methods and windows for the link. [Learn more](https://support.singular.net/hc/en-us/articles/13000445013531-How-to-Build-Tracking-Links#links_attribution_settings). 5. Click **Generate**. 6. In **Link Summary**, review the generated links. If you are setting up [push attribution](https://docs.pushwoosh.com/product/integrations/singular-integration/#set-up-push-attribution-in-pushwoosh), copy the **Click-through tracking link**. You will use it in Pushwoosh. ![Singular Link Summary section with Click-through tracking link highlighted](https://docs.pushwoosh.com/integrations-singular-integration-6.webp) ## Set up push attribution in Pushwoosh Add the Singular tracking link to your push notifications. Paste the **Click-through tracking link** from your **Link Summary** into **On-click actions** → **Deep link or URL** in a [Push preset](https://docs.pushwoosh.com/product/content/push-presets/#define-the-next-step-for-users-who-click-on-the-notification). ![Push notification On-click actions with the Singular tracking link entered in Deep link or URL](https://docs.pushwoosh.com/integrations-singular-integration-1.webp) ## Configure postbacks Singular collects attribution data about where users come from and what they do in the app. With postbacks, you send that data to Pushwoosh automatically. For example, if Singular records that a user installed the app from a specific ad campaign, Pushwoosh can receive this as a tag or event. You can then build a segment of users from that campaign and send them a targeted push in Pushwoosh. 1. In Singular, go to **Partner Configuration**. 2. Create a new configuration or edit an existing one. ![Singular Partner Configuration page for creating or editing a Pushwoosh postback configuration](https://docs.pushwoosh.com/integrations-singular-integration-4.webp) 3. Enter your Pushwoosh **Application Code** and **API Access Token**. Where to find your credentials * **Application Code** (XXXXX-XXXXX): shown below your application name in the Pushwoosh Control Panel. * **API Access Token:** go to **Settings** → **API Access** and copy the token. See [API Access Token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) for details. ![Singular postback configuration form with Pushwoosh Application Code and API Access Token fields](https://docs.pushwoosh.com/integrations-singular-integration-5.webp) 4. In **Attribution Postbacks**, select which events Singular sends to Pushwoosh: * **Install:** new app installs attributed in Singular. Pushwoosh uses this data to record where users came from. * **Re-engagement:** app opens attributed to a tracked link. Enable this if you want re-engagement source data in Pushwoosh for segmentation. [Learn how to configure partner settings and postbacks](https://support.singular.net/hc/en-us/articles/360053018851-How-to-Configure-Partner-Settings-and-Postbacks) in the Singular Help Center. ![Singular Attribution Postbacks table with Install and Re-engagement options for Pushwoosh](https://docs.pushwoosh.com/integrations-singular-integration-7.webp) 5. In **Attribution Windows Settings**, under **Installs**, choose how long Singular can wait after a user clicks your tracking link or views an ad before it stops crediting the install to Pushwoosh. [Learn more](https://support.singular.net/hc/en-us/articles/115000526963-Understanding-Singular-Mobile-App-Attribution). 6. Under **Re-engagements**, choose how long Singular can wait after a click or ad view before it stops crediting the open to Pushwoosh. Enable **Override app level inactivity window for this partner** to set how long the user must be inactive before that open counts as a re-engagement. [Learn more](https://support.singular.net/hc/en-us/articles/360044494651-Re-engagement-FAQ). 7. Open the **In app Events Postback** tab and add the events Pushwoosh should receive (for example `__SESSION__*` for sessions). Enable **Send All** to include users from any source. 8. Save the configuration. ### What Pushwoosh receives After you save, Singular sends postbacks when configured events occur. Pushwoosh processes each postback into device **Tags** and one **Event** on the user profile. * Tags Pushwoosh writes or updates these tags on the matched device: | Tag | Source | | :------------------ | :------------------ | | OS Version | device OS version | | Application Version | app version | | Device Model | device model | | Country | attribution country | | City | attribution city | * Events Pushwoosh records one event on the user profile. The event name depends on the postback type: * **In-app events:** the Singular event name you configured (for example, a session event). * **Install or attribution postback:** if the postback carries no event name, Pushwoosh records `Singular Attribution Event`. Use this event name when you build segments of newly attributed users. Campaign and revenue data travel with the event as **event attributes**. You can segment and filter on them in Pushwoosh: | Attribute | Description | | :-------------------------------- | :---------------------------------- | | Campaign name / Campaign ID | attributed campaign | | Subcampaign name / Subcampaign ID | attributed sub-campaign | | Creative name / Creative ID | attributed creative | | Amount / Revenue / Currency | monetary value of the event, if any | | Country / City | location reported with the event | Device matching Pushwoosh matches each postback to a device using the identifier Singular sends: IDFV on iOS, or GAID, AIFA, or Android ID on Android. The postback must also include your Application Code and platform. If Pushwoosh cannot match the device, the postback is not applied. ### Verify postbacks After Singular sends a postback for a configured event, check that Pushwoosh received it: 1. Open **Audience → Events** and confirm the event appears. 2. Open the user profile in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) and go to [Events history](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#events-history-tab). Confirm the event and its attributes (for example, Campaign name) are recorded for that user. 3. Events usually appear within a few minutes. ## Troubleshooting ### Singular tracking link is not captured * Verify the **Click-through tracking link** is set in **On-click actions** → **Deep link or URL**, not in **Custom Data**. * Confirm the Singular SDK is configured to handle push notification payloads. See the [Android](https://support.singular.net/hc/en-us/articles/35427027157275-Android-SDK-Supporting-Push-Notifications) and [iOS](https://support.singular.net/hc/en-us/articles/42098616285339-iOS-SDK-Configuration-Methods-Reference) guides. ### Deep link opens the app but navigates to the home screen * Confirm the `_dl` parameter inside the Singular tracking link is properly URL-encoded. * Verify your app’s deep link handler is registered correctly for the scheme used (for example, `myapp://`). * On Android, check that the correct `Activity` has the intent filter for your deep link scheme in `AndroidManifest.xml`. ### Attribution appears in Singular but wrong campaign is credited * Make sure the Singular SDK processes the push payload before or during initialization, as described in the Singular SDK guides. * Confirm you are using the correct **Click-through tracking link** from **Link Summary** for this campaign. ### Warm start (app already open): attribution not recorded * Confirm your app handles push notification taps when it is already running in the background, as described in the Singular SDK guides. * This is the most common cause of missing attribution for warm starts. --- # Solitics integration **URL:** https://docs.pushwoosh.com/product/integrations/solitics-integration/ Enhance user engagement and conversion rates with Solitics + Pushwoosh Integration --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [Solitics](https://solitics.com/) is an advanced data management and real-time marketing automation platform that empowers B2C brands to deliver highly personalized experiences to their customers. By leveraging customers’ data, businesses can increase revenue through improved conversion and retention rates, all while significantly reducing development and IT expenses. When combined with Pushwoosh, Solitics’ data management capabilities enable brands to deliver highly targeted and relevant messages to their users. This ensures that users receive content and offers that are tailored specifically to their interests and needs, thus enhancing their overall experience. Brands can also use Solitics to analyze user data and identify areas for improvement in the user journey. By integrating this data with Pushwoosh, brands can send push notifications to re-engage users who may have abandoned their journey, directing them back to the app or website and ultimately improving conversion rates. ## Use cases Below are some practical use cases that demonstrate how the integration of Solitics and Pushwoosh can be used to improve customer experience and drive business outcomes: ### Personalized promotions and offers Businesses can use Solitics’ data to analyze customer preferences and behaviors. Then, they can send customized push notifications through Pushwoosh with tailored promotions and offers based on the individual’s interests. ### Boosting engagement Gaming businesses can configure Solitics to monitor player actions and achievements, then utilize Pushwoosh to promptly deliver push notifications for milestones and special events to players, encouraging active engagement and participation in specific in-game activities. ### Security alerts Banking apps can keep their clients confident in their data security thanks to the Solitics + Pushwoosh integration. They can configure Solitics to monitor account activity and trigger alerts for unusual behavior. Then, they can use Pushwoosh to deliver real-time push notifications to notify affected customers, encouraging swift action to protect their accounts and prevent potential issues. ## Setting up the integration The integration process involves two primary steps: 1. Incorporate the Pushwoosh SDK into your website or mobile app. 2. Establish a user ID that serves as a unique identifier for both Solitics and Pushwoosh. ### Prerequisites 1. Pushwoosh account 2. [Pushwoosh API Access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token): To enable data transmission to Pushwoosh, you need to generate a Pushwoosh API Access token. To get it, go to **Settings > API Access** and copy it there. 3. Pushwoosh App Code: Similarly, you’ll need to locate your Pushwoosh App Code to facilitate data transfer to Pushwoosh. To do so, find the project name in the top left corner and copy the code displayed below it. 4. [Solitics Account](https://dashboard.solitics.com/#/pages/signin) Note To enable the integration, register each user in both Solitics and Pushwoosh using the same member ID. This is the primary requirement for the integration to function effectively. ### Configuring Pushwoosh for integration 1. Set up a Pushwoosh account if you don’t have one. 2. Configure your projects. [Learn more about how to do it.](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms) 3. Next, create an application within Pushwoosh and select the preferred communication channels. You can opt to use an existing application if you already have one. ### Configuring Solitics for integration 1. Contact Solitics () and ask to integrate your brand with Pushwoosh. Specify the following details: * Pushwoosh API Key * Pushwoosh Application Code 2. The Solitics team will provide you with an access token for future interactions with Solitics’ API. ## User registration As emphasized previously, it is important to register users with identical user IDs in both Solitics and Pushwoosh. Note Use the same unique member ID that your brand uses for member integration when registering for push notifications. This consistency will not only ensure that your member ID is unique but will also streamline tracking and facilitate debugging in case of any issues. ### Registration with Pushwoosh Pushwoosh offers two methods for associating a user ID with a specific device. If you do not use these methods, Pushwoosh will automatically employ the device hardware ID as the user’s identifier. To link a user ID to a device in Pushwoosh, you can choose one of the following methods: #### `setUserId` This is an SDK function that enables you to set a string value as a Pushwoosh ID, which will be automatically matched with a Hardware ID (HWID) used by Pushwoosh to identify devices. **Android** [setUserId – Android ](https://github.com/Pushwoosh/pushwoosh-android-sdk/blob/master/Documentation/inapp/PushwooshInApp.md#1a69cdb1592ad7640a49ba8bac8a14d54d) **iOS** [setUserId – iOS ](https://github.com/Pushwoosh/pushwoosh-ios-sdk/blob/master/Documentation/PushNotificationManager.md#1ad51b323a64cbedb408442f9b52cc39fd) #### `registerUser` API Method This method allows you to remotely assign a User ID to a specific HWID from your backend. Detailed information on using this API call can be found [here](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser). ### Register with Solitics When a user is registered for push messages, Solitics also needs to be informed. For this, make the following HTTP request: **HTTP method:** POST **Endpoint:** `https://api.solitics.com/rest/subscribers/register/app` **Request body** ```json { "memberId": "1234567", "email": "abcd@solitics.com", "token": "GH2TKoXXXXXXXEj1111", "key": "1234567” } ``` **Body parameters** | Parameter | Description | | --------- | ------------------------------------------------------------------------------------------------- | | memberID | This represents the unique identifier of the user in Solitics (such as the CRM ID) | | email | The member’s email that is used for two-factor authentication | | token | The token provided by Solitics to get access to the push API. | | key | The member’s push registration key should match the user ID used for registration with Pushwoosh. | While Solitics recommends using the member ID as the key, exceptions may apply. For instance, if multiple applications are used in Pushwoosh, a different key should be assigned to the user for each application. ## Integration testing Once you have completed the setup process, it’s crucial to conduct a test to ensure that the integration functions as expected. Follow these steps for testing: 1. Create a Push Promotion in Solitics and Save. 2. Log in to your app or site. Access the app or website where you anticipate the push message to appear. Ensure that you log into the user account that has been previously registered for push notifications within both Solitics and Pushwoosh. If you are uncertain about the registration status, feel free to contact Solitics at for confirmation, including details about the user’s registration status and associated user ID. 3. Initiate the test. In the Solitics promotion, click the Test button. Enter the email, phone number, or member ID of the subscriber (the intended recipient of the push message). Click Send. If all configurations are correct, the push message should appear. --- # Stripe integration **URL:** https://docs.pushwoosh.com/product/integrations/stripe-integration/ Connect Stripe to track payments, link transactions to campaigns, analyze revenue by journey, and use payment events for segmentation. --- Developer assistance needed You will need help from your development team to pass metadata (journey, user, device) when creating Stripe Checkout Sessions. Share this guide with them. [Stripe](https://stripe.com/) is a payment platform that lets you accept payments and manage subscriptions. Integrating Stripe with Pushwoosh lets you track payments and subscriptions in [campaigns](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), analyze revenue by journey and product, [segment users](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) by payment events, and use [ManyMoney AI](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) for revenue insights. ## Integration overview ### Integration type **Source:** Payment and subscription events are sent from Stripe to Pushwoosh. ### Prerequisites To set up the Stripe integration with Pushwoosh, ensure the following: * You have an active Pushwoosh account. * You have a Stripe account. ### Glossary (mapping entity names if they are different) The table below shows how Stripe entities map to Pushwoosh. This mapping is achieved by passing the corresponding fields as metadata when you create a Checkout Session (see [Metadata configuration](https://docs.pushwoosh.com/product/integrations/stripe-integration/#metadata-configuration)). | Stripe | Pushwoosh | | ------------------ | ------------------------------------------------------------------- | | Customer | `user_id` (required), `device_id` (optional) in metadata | | Payment / Charge | Event **StripePaymentSucceeded** (`charge.succeeded`) | | Invoice (paid) | Event **StripeInvoicePaid** (`invoice.paid`) | | Subscription | **StripeSubscriptionCreated** + attributes in **StripeInvoicePaid** | | Product / Price | `product_id`, `product_name` in metadata and event attributes | | Campaign (journey) | `journey_uuids` in metadata | ### Synchronized entities * Payment events (one-time payments, subscription invoices) * Subscription events (subscription created, subscription invoice paid) ### How does the integration work? After you connect your Stripe account to Pushwoosh via **Stripe Connect**, Pushwoosh receives payment and subscription data from Stripe. You can link each transaction to a campaign and user or device by passing metadata when creating a Checkout Session (see [Metadata configuration](https://docs.pushwoosh.com/product/integrations/stripe-integration/#metadata-configuration)). Pushwoosh creates events you can use for [segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) and analytics. ##### Data flow 1. You connect your Stripe account to Pushwoosh once via **Stripe Connect** in **Settings** → **3rd-party integrations**. 2. When creating a Checkout Session, you pass metadata so the payment can be attributed later (see [Metadata configuration](https://docs.pushwoosh.com/product/integrations/stripe-integration/#metadata-configuration)). 3. When a payment or subscription event occurs in Stripe (e.g. `charge.succeeded` for one-time, `invoice.paid` for subscription), Stripe sends the data to Pushwoosh. 4. Pushwoosh creates the corresponding events and uses the metadata for attribution. This data appears in Finance Overview, Audience → Events, and ManyMoney. ### Use cases ##### Track payments Automatically receive information about all successful payments and subscriptions. ##### Link payments to campaigns Associate transactions with specific [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) by passing metadata (see [Metadata configuration](https://docs.pushwoosh.com/product/integrations/stripe-integration/#metadata-configuration)). ##### Analyze revenue View income by campaigns, products, users, and devices. ##### Segment your audience [Create segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) based on payment events. ##### AI analytics [ManyMoney AI](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) assistant automatically receives payment and subscription statistics and can make decisions based on this data. ## Set up the integration ### Connect Stripe to Pushwoosh 1. Open any Pushwoosh application (Stripe account is linked to your entire account, not a specific application) and navigate to **Settings** → **3rd-party integrations**. 2. Find the **Stripe** card and click the **LOGIN PAGE** button. ![Settings page with 3rd-party integrations section and Stripe card with LOGIN PAGE button](https://docs.pushwoosh.com/integrations-stripe-integration-1.webp) 3. You will be redirected to the Stripe authorization page. ![Stripe authorization page with account selection and Connect button](https://docs.pushwoosh.com/integrations-stripe-integration-2.webp) 4. On the Stripe page, enter your email and click **Continue**. 5. Log in to your Stripe account (or create a new one). If you have multiple accounts, select the account you want to connect. 6. Click **Connect** to confirm. 7. After successful authorization, you will be redirected back to Pushwoosh. The integration status will change to **Connected**. ![3rd-party integrations page showing Stripe card with Connected status](https://docs.pushwoosh.com/integrations-stripe-integration-3.webp) ### Disconnect the integration ##### Method 1. Via Pushwoosh 1. Go to **Settings** → **3rd-party integrations**. 2. Find the **Stripe** card and click the **SETTINGS** button. 3. In the popup window, click the **Disconnect** button. ![Stripe card SETTINGS popup with Disconnect button in 3rd-party integrations](https://docs.pushwoosh.com/integrations-stripe-integration-4.webp) ##### Method 2. Via Stripe Dashboard 1. Log in to [Stripe Dashboard](https://dashboard.stripe.com). 2. Go to **Settings** → **Team and security** → **Installed apps**. 3. Find the application in the **Connect Extensions** section. ![Stripe Dashboard Settings, Team and security, Installed apps, Connect Extensions section](https://docs.pushwoosh.com/integrations-stripe-integration-5.webp) When you disconnect through Stripe, Pushwoosh automatically receives a notification and removes the integration. ## Metadata configuration Stripe sends payment events to Pushwoosh, but without extra data Pushwoosh cannot tell which campaign or which user the payment belongs to. When you pass metadata at Checkout Session creation (campaign IDs, user or device ID, product), each payment is attributed to the right journey and user. You then see revenue by campaign in Finance Overview, build segments by payer, and use ManyMoney with correct attribution. ### Available metadata fields | Field | Description | Required | Example | | --------------- | -------------------------------------------------------------------------------- | -------- | --------------------------------------------------------------------------- | | `journey_uuids` | Campaign (journey) IDs separated by semicolons | No | `bfab4bc0-b0a5-414b-befc-4aaddc429b0e;a2bff710-6b49-44d1-96a7-3232feeca6e9` | | `user_id` | User identifier. Required for event collection and for `device_id` to be applied | Yes | `user_12345` or `email@example.com` | | `device_id` | Device Hardware ID (HWID). | No | `hwid_abc123` | | `product_id` | Product ID | No | `prod_premium` | | `product_name` | Product name | No | `Premium Plan` | Important * Without `user_id`, events are not collected and `device_id` is ignored. For comprehensive analytics, also provide `journey_uuids` and `device_id`. * `journey_uuids` is optional and can only be set via metadata. Stripe does not supply campaign or journey data, so pass it when creating the Checkout Session if you want revenue attributed to a journey. * `product_id` and `product_name` are optional. Pushwoosh uses metadata first. If either is missing in metadata, it is taken from Stripe when available. If neither source has a value, the field is not stored. ### Passing metadata via Checkout Session Metadata is passed when creating a Checkout Session depending on the payment type: | Payment type | Parameter | Stripe event | | ---------------------------------- | ------------------------------- | ------------------ | | One-time payment (`mode=payment`) | `payment_intent_data[metadata]` | `charge.succeeded` | | Subscription (`mode=subscription`) | `subscription_data[metadata]` | `invoice.paid` | ### Metadata priority during processing **For subscriptions** (`invoice.paid` event): ```text Invoice metadata → if empty → Subscription metadata ``` **For one-time payments** (`charge.succeeded` event): ```text Charge metadata (from payment_intent_data) ``` ## Creating a checkout session via Stripe API (curl) ##### One-time payment (`mode=payment`) ```bash curl https://api.stripe.com/v1/checkout/sessions \ -u sk_live_YOUR_SECRET_KEY: \ -d "mode=payment" \ -d "success_url=https://example.com/success" \ -d "cancel_url=https://example.com/cancel" \ -d "line_items[0][price]=price_1234567890" \ -d "line_items[0][quantity]=1" \ -d "payment_intent_data[metadata][journey_uuids]=bfab4bc0-b0a5-414b-befc-4aaddc429b0e" \ -d "payment_intent_data[metadata][user_id]=user_12345" \ -d "payment_intent_data[metadata][device_id]=hwid_abc123" \ -d "payment_intent_data[metadata][product_id]=prod_premium" \ -d "payment_intent_data[metadata][product_name]=Premium Plan" ``` ##### Subscription (`mode=subscription`) ```bash curl https://api.stripe.com/v1/checkout/sessions \ -u sk_live_YOUR_SECRET_KEY: \ -d "mode=subscription" \ -d "success_url=https://example.com/success" \ -d "cancel_url=https://example.com/cancel" \ -d "line_items[0][price]=price_monthly_premium" \ -d "line_items[0][quantity]=1" \ -d "subscription_data[metadata][journey_uuids]=bfab4bc0-b0a5-414b-befc-4aaddc429b0e" \ -d "subscription_data[metadata][user_id]=user_12345" \ -d "subscription_data[metadata][device_id]=hwid_abc123" \ -d "subscription_data[metadata][product_name]=Monthly Premium" ``` ## View data After successful integration, a new **Finance Overview** dashboard appears in the [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) section. There you can view Gross Revenue and New Subscriptions statistics broken down by campaigns (journey). ![Finance Overview dashboard in Statistics with Gross Revenue and New Subscriptions by campaign](https://docs.pushwoosh.com/integrations-stripe-integration-6.webp) For more detailed information, visit your Stripe Dashboard. ## Create segments based on payments Use Stripe events to create user segments: 1. Open **Audience** → **Segments**. 2. Click **Create Segment** to open the segment builder. 3. In **Add filter by**, click **Event**. 4. Select a Stripe event from the dropdown (see the table below for available events). Note Stripe events appear in the list after the integration is connected and payment data is received. 5. Set the condition: how many times the event occurred and the time frame (e.g. during the last 30 days, between dates). 6. Optionally narrow the segment by event attributes. The table below lists the attributes available for each event. | Event | Description | Attributes | | --------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | | `StripePaymentSucceeded` | Successful payment | __amount, __currency, invoice_id, journey_uuids, product_id, product_name, stripe_customer_id, subscription_id | | `StripeInvoicePaid` | Subscription invoice paid | __amount, __currency, journey_uuids, product_id, product_name, stripe_customer_id, transaction_id, transaction_type | | `StripeSubscriptionCreated` | Subscription created | __amount, __currency, interval, journey_uuids, product_id, product_name, status, stripe_customer_id, subscription_id | ![Segment conditions showing a StripePaymentSucceeded event filter with amount and currency attributes](https://docs.pushwoosh.com/integrations-stripe-integration-7.webp) 7. To add more events, add another Event filter and choose an operator (AND or OR) between conditions. [Learn more about creating segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/). Tip You can also track revenue for `StripePaymentSucceeded` (`__amount`, `__currency`, `product_id`) or `StripeInvoicePaid` (`__amount`, `__currency`, `transaction_id`, `product_id`) as [conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/), to bring Stripe revenue into RFM segmentation, journey attribution, and ManyMoney AI alongside your other revenue sources. ## ManyMoney AI Assistant After successful Stripe integration, the [**ManyMoney**](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) AI assistant automatically gains access to payment and subscription statistics. ManyMoney is available in the Dashboard interface. After you connect Stripe, payment data is available for analysis automatically. No additional configuration is required. ### What ManyMoney can do * **Analyze revenue:** answers questions about income, conversions, and campaign effectiveness. * **Compare periods:** shows payment and subscription dynamics across different time intervals. * **Identify trends:** detects growing and declining products and audience segments. * **Provide recommendations:** suggests optimizations based on payment data. Prompt examples * What revenue did the campaign generate last month? * Compare subscription conversion rates between January and February * Show refund statistics --- # WebViewGold integration **URL:** https://docs.pushwoosh.com/product/integrations/webviewgold-integration/ Use WebViewGold to convert web content into mobile apps and integrate Pushwoosh to boost user engagement and retention. --- Developer assistance needed You’ll need help from your development team to set up the integration. Please share this guide with them. [WebViewGold](https://www.webviewgold.com/) simplifies the process of converting web content into functional mobile applications for both iOS and Android. You don’t need to have any coding skills to use this solution, which makes mobile app development accessible to a wider audience. WebViewGold offers templates for creating mobile apps, automating the app development process. Once created, the apps can be distributed on major app stores, such as the App Store and Google Play. Thanks to the integration with Pushwoosh, WebViewGold users can now jumpstart powerful omnichannel communication for their web apps. Note **Free license upgrade for Pushwoosh users!** Get a free license upgrade for WebViewGold! Simply send your WebViewGold License Code and Pushwoosh App Code to to claim this offer. To give you a better idea of how to utilize the integration to achieve your business goals, here are some examples: ### Boost engagement WebViewGold apps can keep users coming back by using Pushwoosh to send triggered and scheduled campaigns. This means, for instance, sending a push notification for a special promotion exactly when a user performs a certain action on the app. ### Onboard users New users of apps created with WebViewGold templates can be welcomed with a series of in-app messages and push notifications that help to understand the core values of the product, potentially increasing user activation rates. ### Collect feedback WebViewGold apps can use Pushwoosh to send surveys to gather user feedback and insights for product development. ### Increase retention Pushwoosh’s segmentation based on trigger events can identify when users become less active in WebViewGold apps and send re-engagement communications to prevent churn. ## Setting up push notifications For incorporating push notifications into your WebViewGold app, detailed setup guides are available: ### For Android To configure push notifications for your Android app, please follow the instructions provided in the [WebViewGold documentation](https://www.webviewgold.com/docs/android/#pushwoosh) ### For iOS To set up push notifications for your iOS app, follow the step-by-step [guide in the WebViewGold documentation](https://www.webviewgold.com/docs/iOS/#pushwoosh) --- # WooCommerce integration **URL:** https://docs.pushwoosh.com/product/integrations/woocommerce-integration/ Sync WooCommerce customers, orders, and abandoned carts to Pushwoosh and add storefront web push with the official plugin. --- The Pushwoosh for WooCommerce plugin connects your WordPress store to Pushwoosh. Customers become email and SMS contacts, orders and abandoned carts become events you can start Customer Journeys from, and your storefront gets web push with a subscribe bell. Developer assistance needed You’ll need help from your development team or the person who runs your WordPress site to install the plugin and set up a few server-side options. Please share this guide with them. ## Integration overview [WooCommerce](https://woocommerce.com/) is an open-source e-commerce plugin for WordPress. The [Pushwoosh for WooCommerce](https://wordpress.org/plugins/pushwoosh-for-woocommerce/) plugin sends your store data to Pushwoosh directly through the Pushwoosh API. The plugin sends data in the background, so it doesn’t slow down checkout. ### Integration type **Destination**: This integration pushes data from your WooCommerce store to Pushwoosh. ### Prerequisites * WordPress 6.4 or later, WooCommerce 8.2 or later, and PHP 8.1 or later * Administrator access to your WordPress site * A Pushwoosh account with an application for your store * An HTTPS site address, if you want web push. Browsers only register web push on HTTPS sites. The plugin works with High-Performance Order Storage (HPOS) and with both the classic and the block-based Cart and Checkout pages. ### What the plugin syncs The table shows what each WooCommerce entity becomes in Pushwoosh: | WooCommerce | Pushwoosh | | :------------------------------------------ | :------------------------------------------------------------------------------------------- | | Customer, or guest buyer | User with an email contact, plus an SMS contact if the phone number is a valid mobile number | | Customer name, consent, and mapped fields | Tags | | Order created, updated, or canceled | `PW_OrderCreated`, `PW_OrderUpdated`, `PW_OrderCanceled` events | | Abandoned cart | `PW_AbandonedCart` and `PW_AbandonedCartUpdate` events with a link that restores the cart | | Storefront visitor who allows notifications | Web push subscriber linked to the customer’s email | The user ID in Pushwoosh is the customer’s email address in lowercase: the account email for registered customers, the billing email for guests. Contacts, events, and web push subscribers of one customer all land on the same user. ### Use cases * Send an email or push when a shopper leaves items in the cart, with a button that restores that cart. * Thank buyers after an order, follow up when an order ships, or win back customers after a cancellation. * Segment customers by city, country, or any customer field you map to a tag. * Send web push promotions to storefront visitors who subscribed through the bell. ## Prepare your Pushwoosh application Do these steps before you turn on the plugin. If the plugin sends an event that doesn’t exist yet, Pushwoosh creates it automatically and guesses attribute types from the first values it receives. A guessed type can be wrong (for example, a price saved as an integer), and data sent before you fix the type is lost. ### Create API tokens The plugin uses two [Device API tokens](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#device-api-token). Create both in **Settings → API Access** and give them access to your application: 1. Create a Device API token for the server. The plugin uses it to register contacts and send events. It never leaves your server. 2. Create a second Device API token for the storefront. The plugin prints it into every storefront page to start web push, so it’s public. A separate token lets you replace it without touching the server token. A [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) is optional. With it, the plugin settings show the list of tags in your application, which helps when you map customer fields to tags. ### Create the events Create five custom events in **Audience → Events → Create Event → Custom Event**: `PW_OrderCreated`, `PW_OrderUpdated`, `PW_OrderCanceled`, `PW_AbandonedCart`, and `PW_AbandonedCartUpdate`. [Learn how to create custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/#creating-custom-events). Give each event the attributes it receives, with these types: | Attribute | Type | Events | | :-------------------------------------------------- | :-------------- | :----------- | | `orderId` | string | All | | `source` | integer | All | | `orderNumber`, `status`, `wcStatus` | string | Order events | | `email`, `customerId`, `shippingMethod`, `currency` | string | All | | `orderUrl` | string | Cart events | | `totalPrice`, `shippingAmount`, `discountAmount` | decimal (price) | All | | `taxAmount` | decimal (price) | Order events | | `createdDate` | date | All | | `updatedDate` | date | Order events | | `abandonedDate` | date | Cart events | | `items` | list | All | | `historical` | boolean | Order events | If `orderId` is already set to integer in your order events, change it to string. With integer, Pushwoosh doesn’t count the order’s items as sales for bestseller recommendations in the email Products block. ### Create the tags Create these tags on the [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#how-to-set-up-a-custom-tag) page: | Tag | Type | | :----------------- | :------ | | `firstName` | string | | `lastName` | string | | `acceptsMarketing` | integer | Also create a tag for every customer field you plan to map, as described in [Map customer fields to tags](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#map-customer-fields-to-tags). The default mapping uses the `City` and `Country` tags. ### Configure web push To use web push, set up the Web platform for your application. See [Web push configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/). ## Install the plugin 1. In your WordPress admin panel, go to **Plugins → Add New Plugin**. 2. Search for **Pushwoosh for WooCommerce**. 3. Click **Install Now**. 4. Click **Activate**. WooCommerce must already be active. You can also download the plugin zip from [wordpress.org](https://wordpress.org/plugins/pushwoosh-for-woocommerce/) and upload it in **Plugins → Add New Plugin → Upload Plugin**. Low-traffic stores The plugin sends data in the background with WooCommerce’s Action Scheduler, which runs on WP-Cron. WP-Cron only fires when someone visits the site, so on a store with few visitors, events can arrive late. Ask your developer to run WP-Cron from a system cron job instead: set `DISABLE_WP_CRON` to `true` in `wp-config.php` and run `wp cron event run --due-now` every minute. ## Connect the plugin to Pushwoosh Go to **WooCommerce → Settings → Pushwoosh** and fill in the **Connection** section: 1. Leave **API URL** as `https://api.pushwoosh.com` unless Pushwoosh support gave you a different address. 2. In **Application code**, enter your [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). 3. In **Device API token**, paste the server token you created. 4. In **Web SDK token**, paste the storefront token. 5. Optionally, in **Server API token (optional)**, paste a Server API token. 6. Click **Save changes**. 7. Click **Check connection**. ![Connection section of the Pushwoosh settings in WooCommerce with the Check connection results](https://docs.pushwoosh.com/woocommerce-integration-1.webp) **Check connection** verifies: * The API URL * The application code and the token * The send queue * The web push service worker, if web push is turned on It doesn’t send test events, because a test event would create the events with empty attributes. If the check says the token couldn’t be verified, your application accepts any Device API token. Ask Pushwoosh support to turn on Device API token checking for the application. Saved tokens are shown masked. To keep a saved token, leave its field empty when you save the settings. ## Turn on the modules In the **Modules** section, turn on the data you want to send, then click **Save changes**: * **Sync customers:** registers customers as contacts in Pushwoosh when they sign up and every time their profile is saved. * **Send order events:** sends order events. Only orders created after you turn this on get `PW_OrderCreated`. To send past orders, see [Import existing customers and orders](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#import-existing-customers-and-orders). * **Send abandoned cart events:** sends cart events. Set **Cart is abandoned after, minutes** to how long a cart must stay untouched before `PW_AbandonedCart` is sent. The default is 60, the minimum is 15. * **Enable web push on the storefront:** loads web push on every storefront page. See [Set up web push](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#set-up-web-push). * **Require marketing consent:** sends nothing for customers who didn’t agree to marketing. See [Collect marketing consent](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#collect-marketing-consent). * **Delete all data when the plugin is deleted:** when you delete the plugin, it also removes its settings, its data in user and order records, and queued jobs. The table of tracked carts is removed whether this is on or not. Data already in Pushwoosh stays. ![Modules section of the Pushwoosh settings in WooCommerce with module checkboxes, abandoned cart minutes, and web push options](https://docs.pushwoosh.com/woocommerce-integration-2.webp) ### Map customer fields to tags In the **Customer tags** section, use **Field → tag mapping** to send more customer data as tags. Write one `source = Tag` pair per line: ```plaintext billing_city = City billing_country = Country shipping_postcode = Postcode meta:loyalty_level = Loyalty level ``` * **Source** is a customer field, such as `billing_city` or `shipping_postcode`, or `meta:` followed by a user or order meta key. * **Tag** is the name of a tag that already exists in your Pushwoosh application. `firstName`, `lastName`, and `acceptsMarketing` are always sent, so you don’t need to map them. With a Server API token saved, the section lists the tags that exist in your application, and it shows any mapping lines it ignored. ### Collect marketing consent The plugin adds the checkbox **I agree to receive news and offers by email and SMS** to the registration form and to both the classic and the block-based checkout. The answer is stored on the customer and the order, and sent as the `acceptsMarketing` tag (`1` or `0`). A customer who already agreed doesn’t see the checkbox again, and an unticked box on a later order doesn’t withdraw an earlier agreement. ![WooCommerce checkout with the unticked marketing consent checkbox for email and SMS above the Place Order button](https://docs.pushwoosh.com/woocommerce-integration-3.webp) What **Require marketing consent** changes: * **Off (default):** every customer is registered as a contact, and `acceptsMarketing` tells you who agreed. Use it to filter your audience in segments. * **On:** customers without consent get no email or SMS contact, their orders send no events, and their carts send no cart events. When you turn the option on, existing contacts aren’t removed right away. A registered customer synced earlier without consent is deleted from Pushwoosh the next time their profile is saved, or when you run the customer import again. Guest contacts aren’t deleted. The block-based checkout shows the checkbox on WooCommerce 8.9 and later. On earlier versions with **Require marketing consent** on, carts from that checkout send no events. ## Events and tags the plugin sends This section lists every event and tag the plugin sends, so you can build segments and journeys on them. ### Contacts and tags The plugin registers each customer as an email contact and, when the billing phone is a valid mobile number, as an SMS contact. The contact’s language is the store language the customer used when they registered or checked out. Each contact gets these tags: | Tag | Value | | :----------------- | :------------------------------------------------------------------------- | | `firstName` | Billing first name, or the account first name if billing is empty | | `lastName` | Billing last name, or the account last name if billing is empty | | `acceptsMarketing` | `1` if the customer agreed to marketing, otherwise `0` | | Mapped tags | Values of the fields in your [field mapping](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#map-customer-fields-to-tags) | The plugin syncs customers at these moments: * **Registered customers:** when they sign up and each time their profile is saved. * **Guest buyers:** from their order, with **Sync customers** on. * **Guests who abandon a cart:** right before the cart event, so the event has a user to land on. * **Customers who change their account email:** the plugin deletes the old user in Pushwoosh and registers the new one. Pushwoosh rejects some contacts and the plugin doesn’t retry them: emails on invalid or disposable domains and phone numbers that aren’t mobile. These are logged as described in [Check that the integration works](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#check-that-the-integration-works). ### Order events The plugin sends one event each time an order changes status: | Event | When it’s sent | | :----------------- | :---------------------------------------------------------------------------------------------------------------------------------- | | `PW_OrderCreated` | The first time an order created after you turned on **Send order events** is synced, in whatever status it has then | | `PW_OrderUpdated` | Every later status change except cancellation. Also the first event of an order created before you turned on **Send order events**. | | `PW_OrderCanceled` | The order moves to **Cancelled** | Draft and failed orders send no events. Pushwoosh also counts the items of `PW_OrderCreated` and `PW_OrderUpdated` as sales for bestseller recommendations in the email Products block. The `status` attribute uses the same values as the Magento integration, so journeys built for Magento work without changes. The original WooCommerce status is in `wcStatus`: | WooCommerce status | `status` | | :----------------------- | :-------------------- | | Pending payment, On hold | `pending` | | Processing | `processing` | | Completed | `complete` | | Refunded | `closed` | | Cancelled | `canceled` | | Custom status | The status slug as is | Every order event carries these attributes: | Attribute | Value | | :---------------------------------------------- | :------------------------------------------------------------------ | | `orderId` | Order ID | | `orderNumber` | Order number shown to the customer | | `source` | Always `1` | | `email` | Billing email | | `customerId` | The user ID: account email, or billing email for guests | | `items` | Ordered products, see below | | `createdDate`, `updatedDate` | Date the order was created and last changed, in the store time zone | | `shippingMethod` | Name of the first shipping method | | `totalPrice` | Order total | | `shippingAmount`, `taxAmount`, `discountAmount` | Shipping, tax, and discount totals | | `currency` | Order currency code | | `status`, `wcStatus` | Order status, see the table above | | `historical` | `true` for past orders sent by the import, otherwise `false` | Each entry in `items` has `productId`, `sku`, `name`, `description` (short description), `price` (unit price without tax), `quantity`, `imageUrl`, `productUrl`, and `category` (category names separated by commas). Example `PW_OrderCreated` attributes: ```json { "orderId": "1042", "orderNumber": "1042", "source": 1, "email": "jane@example.com", "customerId": "jane@example.com", "items": [ { "productId": "17", "sku": "TSHIRT-M", "name": "T-shirt", "description": "Cotton T-shirt", "price": 20.0, "quantity": 2, "imageUrl": "https://shop.example.com/wp-content/uploads/tshirt.jpg", "productUrl": "https://shop.example.com/product/t-shirt/", "category": "Clothing, T-shirts" } ], "createdDate": "2026-09-30T12:00:00+02:00", "updatedDate": "2026-09-30T12:00:00+02:00", "shippingMethod": "Flat rate", "totalPrice": 47.6, "shippingAmount": 5.0, "taxAmount": 7.6, "discountAmount": 5.0, "currency": "EUR", "status": "pending", "wcStatus": "on-hold", "historical": false } ``` ### Abandoned cart events A cart counts as abandoned when it has an email, stays untouched longer than **Cart is abandoned after, minutes**, and no order is placed from it: | Event | When it’s sent | | :----------------------- | :-------------------------------------------------------------------- | | `PW_AbandonedCart` | The first time a cart is abandoned | | `PW_AbandonedCartUpdate` | The same cart changed after `PW_AbandonedCart` and is abandoned again | The plugin checks carts every 15 minutes, so the event normally arrives within 15 minutes after the threshold. If the shopper places an order before that, from the same session or with the same billing email, the cart is closed and no event is sent. If the shopper fills a closed cart again, it starts over with `PW_AbandonedCart`. The plugin needs the shopper’s email to send a cart event: * **Logged-in shopper:** the account email. * **Guest:** the email typed into the billing email field at checkout, even if the order is never placed. Cart event attributes are `orderId` (cart ID), `source`, `email`, `customerId`, `items`, `createdDate`, `abandonedDate` (when the cart last changed), `shippingMethod`, `totalPrice`, `shippingAmount`, `discountAmount`, `currency`, and `orderUrl`. In cart events, item prices include tax. #### Cart recovery link `orderUrl` is a link that restores the shopper’s cart. Use it as the button link in your reminder: * Opening the link puts the saved items in the cart and takes the shopper to checkout. It works in any browser, including one where the shopper never visited your store. * The link doesn’t log anyone in. * The link is valid for 14 days. After an order is placed from the cart, or after the cart is deleted, the link opens the visitor’s ordinary cart page instead. Recovery links stop working when you delete the plugin, even if you install it again, because the key that signs them is deleted with the plugin. ## Build an abandoned cart journey This example sends an email with the cart items and a button that restores the cart. It needs both **Send abandoned cart events** and **Send order events** turned on, so the journey can tell when the shopper bought. ### Create the email Create an email in the [email content editor](https://docs.pushwoosh.com/product/content/email-content/), then show the cart items in one of two ways: * **Products block:** shows the cart as Pushwoosh stores it at send time. If the shopper changed the cart after `PW_AbandonedCart`, the email shows the latest items. * **Liquid:** shows the items from the `PW_AbandonedCart` event that started the journey, in any layout you write yourself. #### Show the items with the Products block 1. Add a [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block to the email. 2. Set **Source** to [Cart](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart). 3. Set **Max products shown**. The **Cart** source needs **Content Templates** on your plan and an [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) in **Settings → API Access**. Without a token, the email can’t be saved. #### Show the items with Liquid Add the cart contents to the email body with Liquid. The placeholders take their values from the `PW_AbandonedCart` event that starts the journey: ```liquid

Your cart total is {{ totalPrice }} {{ currency }}.

{% for item in items %} {% endfor %}
{{ item.name }} × {{ item.quantity }} {{ item.price }} {{ currency }}
``` #### Add the recovery button The Products block doesn’t add a link that restores the cart, so add this button with either way of showing the items: 1. Add a button to the email. 2. Set the button link to `{{ orderUrl }}`. 3. Save the email. ### Build the journey 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) with the `PW_AbandonedCart` event. 2. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step. 3. Set its waiting period, for example to 1 hour. 4. In **Wait for Trigger**, name the branch **Order placed**. 5. Add the `PW_OrderCreated` event to the **Order placed** branch. 6. End the **Order placed** branch with an [Exit](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element or by leaving it without an outgoing connection. The shopper already bought. 7. Route the **Not triggered** branch to an [Email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) step. 8. In the Email step, select the email you [created](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#create-the-email). 9. In the Email step, turn on **Overwrite Liquid placeholders** and select the `PW_AbandonedCart` event. 10. End the branch after the Email step with an **Exit** element or by leaving it without an outgoing connection. 11. Launch the journey. The plugin already waits for **Cart is abandoned after, minutes** before it sends `PW_AbandonedCart`. The waiting period in **Wait for Trigger** adds to that time, so keep it short. Set the waiting period on **Wait for Trigger** itself, without a separate **Time Delay** step before it. **Wait for Trigger** only catches `PW_OrderCreated` sent after the shopper reaches it. A shopper who orders during a **Time Delay** still gets the reminder, and a Products block in it shows no items, because the order clears the `PW_AbandonedCartID` tag the block reads the cart by. To remind the shopper with a push instead, use a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) step and turn on **Personalize message with event attributes** to insert `orderUrl` or `totalPrice`. See [Dynamic Content and Liquid Templates in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/). ## Set up web push With web push turned on, every storefront page loads the Pushwoosh Web SDK, and visitors can subscribe to notifications from your store. Choose how visitors are asked to subscribe: * **Subscribe bell on the page (works in every browser):** shows a bell in the corner of the page. The browser asks for permission after the visitor clicks it. This is the default. * **Ask on page load (Chrome and Edge only):** the browser asks for permission as soon as the page opens. Safari, and Firefox in most cases, only show a permission request after a click, so visitors in those browsers aren’t asked at all. To turn on web push: 1. Make sure the Web platform is configured in your application, as described in [Configure web push](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#configure-web-push). 2. In **WooCommerce → Settings → Pushwoosh**, fill in **Web SDK token**. 3. Turn on **Enable web push on the storefront**. 4. In **Web push subscription**, choose how visitors are asked to subscribe. 5. Click **Save changes**. 6. Click **Check connection** to make sure the service worker loads. ![Storefront page with the green Pushwoosh subscribe bell in the lower-left corner and its prompt text next to it](https://docs.pushwoosh.com/woocommerce-integration-4.webp) You can change the bell text, the subscription prompt, the default notification title and image, and the Safari settings in your application’s [web push configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/#configure-the-subscription-prompt). Those settings override the plugin’s defaults. ### How subscribers are linked to customers A new subscriber is anonymous at first. The plugin links the subscriber to the customer’s user in Pushwoosh: * After a customer logs in. * On the order confirmation page, for every buyer, including guests. When a customer logs out or withdraws consent, the link made at login is removed. With **Require marketing consent** on, a subscriber is linked only when the customer or the order has consent. ### Service worker The plugin serves the web push service worker from `/pushwoosh-sw/` on your site and excludes it from WP Rocket, LiteSpeed Cache, and W3 Total Cache page caching. If **Check connection** reports a wrong status for the service worker, ask your developer to check that the URL isn’t blocked or cached by the server. The service worker covers the whole site, so it replaces any other service worker registered at the site root, such as one from a PWA plugin or another push provider. Your developer can add that other service worker’s scripts with the `pushwoosh_wc_service_worker_imports` filter. ## Import existing customers and orders Send customers and orders that existed before you installed the plugin from the **Historical sync** section of **WooCommerce → Settings → Pushwoosh**: * **Sync existing customers:** registers all customers with the **Customer** role, then guest buyers of past orders. * **Send past orders:** sends one `PW_OrderCreated` event with `historical` set to `true` for each past order that never had one. Cancelled orders are skipped. The section shows the progress of each run. Both runs only queue the calls, and Action Scheduler sends them in the background. Filter past orders out of order journeys Every past order sends `PW_OrderCreated`. In journeys that start on this event, add an event attribute condition `historical` equals `false` to the entry. Otherwise, customers get “thank you for your order” messages for old orders. On a large store, ask your developer to run the import with WP-CLI. Add `--since=YYYY-MM-DD` to import only records from that date: ```shell wp pushwoosh sync customers --since=2025-01-01 wp pushwoosh sync orders --historical --since=2026-01-01 ``` ## Export and erase personal data The plugin works with the WordPress personal data tools in **Tools → Export Personal Data** and **Tools → Erase Personal Data**: * **Export** includes the data the plugin keeps about the customer: the synced email, the consent answer, the language, and the customer’s tracked carts. * **Erase** deletes that data and the customer’s tracked carts, cancels pending Pushwoosh calls for the email, and deletes the user and their contacts in Pushwoosh. The customer isn’t synced again unless they tick the consent checkbox at a later checkout. Erasing doesn’t remove orders. An order WooCommerce keeps still sends order events with its billing email when its status changes. Whether orders are anonymized is set in WooCommerce’s own privacy settings. Add the data you send to Pushwoosh to your store’s privacy policy. ## Check that the integration works 1. Place a test order with an email you can find, and tick the consent checkbox. 2. Open [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) and search for that email. 3. Check that the user has an email contact with the `firstName`, `lastName`, and `acceptsMarketing` tags. 4. Check that the user’s events include `PW_OrderCreated`. If data doesn’t arrive, open **WooCommerce → Status → Logs** and select the `pushwoosh` source. The log shows each failed call with the reason. Tokens are never logged, and emails are masked. How the plugin handles errors: * **Network errors and temporary server errors:** the call is retried after 1, 5, 15, 60, and 240 minutes, then logged as an error. * **Rejected data**, such as a contact on a disposable email domain: logged as an error and not retried. * **Rejected token:** sending pauses, and the WordPress admin shows a notice with a **Resume sending** button. Queued calls are kept. Fix the token in the settings, then click **Resume sending**. --- # Understanding messaging channels in Pushwoosh **URL:** https://docs.pushwoosh.com/product/messaging-channels/ Use push, in-app, email, SMS, Web popups, WhatsApp, Telegram, LINE, Facebook Messenger, and more in omnichannel campaigns. --- Pushwoosh empowers you to connect with your users across multiple messaging channels, helping you deliver timely, personalized, and impactful communications. With Pushwoosh, you can leverage the following channels: ## Available messaging channels ### Core messaging channels [Push notifications ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/) [Reach users instantly on their mobile devices or web browsers with timely and engaging push notifications.](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/) [Message inbox ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) [Provide a centralized hub for users to access past push notifications and messages at their convenience.](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) [Feed campaigns ](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) [Send every new item from an RSS or Atom feed to your subscribers automatically, as a push, an inbox message, or both.](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) [In-app messages ](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) [Engage users within your app by delivering real-time updates, promotions, or personalized offers.](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) [Web popups ](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) [Show targeted Web popups on your website with content, audience rules, and display timing.](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) [Email ](https://docs.pushwoosh.com/product/messaging-channels/emails/) [Send tailored emails for onboarding, promotions, order confirmations, and more.](https://docs.pushwoosh.com/product/messaging-channels/emails/) [Apple Wallet passes ](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) [Design loyalty cards, coupons, and tickets as Apple Wallet passes, then push live updates to every device.](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) [Google Wallet passes ](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) [Design loyalty cards, offers, and tickets as Google Wallet passes, then update them and let Google sync every device.](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) ### Messenger chats and related channels [WhatsApp ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/) [Communicate with users on one of the most popular messaging platforms, ensuring reliable and engaging conversations.](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/) [LINE ](https://docs.pushwoosh.com/product/messaging-channels/line/) [Send LINE messages via Customer Journeys, API, or direct 1:1 chat.](https://docs.pushwoosh.com/product/messaging-channels/line/) [Kakao ](https://docs.pushwoosh.com/product/messaging-channels/kakao/) [Send Kakao messages via Customer Journeys or API using approved templates.](https://docs.pushwoosh.com/product/messaging-channels/kakao/) [Viber ](https://docs.pushwoosh.com/product/messaging-channels/viber/) [Send Viber messages via Customer Journeys using Rakuten-approved templates or freeform text.](https://docs.pushwoosh.com/product/messaging-channels/viber/) [Telegram ](https://docs.pushwoosh.com/product/messaging-channels/telegram/) [Connect a Telegram bot and reach users on Telegram from your Pushwoosh project.](https://docs.pushwoosh.com/product/messaging-channels/telegram/) [SMS ](https://docs.pushwoosh.com/product/messaging-channels/sms/) [Deliver quick, time-sensitive updates directly to your users’ phones.](https://docs.pushwoosh.com/product/messaging-channels/sms/) [Facebook Messenger ](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/) [Send Facebook Messenger messages via Customer Journeys or one-time messages.](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/) ## Which channel to use for which goal Use this table to pick a channel and see what has to be ready before the first send. Limits and delivery conditions are on each channel’s page. | Channel | Use it for | What you need first | | --------------------------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------------------------------------------------- | | [Push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/) | Alerts and reminders outside the app | Pushwoosh SDK (mobile or Web), platform credentials, subscribed users | | [In-app messages](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) | Messages while the user is in the app | Pushwoosh SDK in the mobile app | | [Message inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) | Messages users can read later | Pushwoosh SDK (mobile or Web) with message inbox | | [Feed campaigns](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) | Automatic messages for each new item in an RSS or Atom feed | A registered news feed, plus push or message inbox set up | | [Email](https://docs.pushwoosh.com/product/messaging-channels/emails/) | Newsletters, long content, receipts | Verified domain, email subscribers | | [Web popups](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) | Offers and signup forms on your website | Pushwoosh Web SDK on the website | | [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms/) | Short, urgent messages to any phone | Connected SMS provider, phone numbers | | [WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/) | Template notifications and conversations | Connected WhatsApp Business Account, phone numbers | | [Other messengers](https://docs.pushwoosh.com/product/messaging-channels/#messenger-chats-and-related-channels) | Users of Telegram, LINE, Kakao, Viber, or Facebook Messenger | Connected bot, account, or Facebook Page. Viber also needs MStat connected as the SMS provider | | [Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) | Loyalty cards, coupons, tickets | Apple Pass Type ID certificate or Google Wallet issuer account | ## Combine channels for maximum impact Pushwoosh allows you to seamlessly combine these messaging channels to create effective, omnichannel campaigns. By delivering the right message at the right time through the most suitable channel, you can maximize user engagement, improve retention, and boost conversion rates. Whether it’s sending a push notification to re-engage a user, following up with an email, or delivering a final SMS reminder—Pushwoosh gives you the tools to build cohesive, cross-channel messaging strategies that drive results. [Learn more about omnichannel campaigns](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) ## Limit how many messages users receive Avoid overwhelming your users with too many messages by using **Global frequency capping**. This feature limits the number of messages per channel within a set timeframe. Once the limit is reached, no further messages are sent until the period resets. You have full control to set custom limits for each channel to match your engagement strategy and exclude critical messages like transactional updates, security alerts, or urgent notifications to ensure they always get delivered. [Learn more about Global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) ## Decide if a message is marketing or transactional For Push, Email, and In-app you choose whether each message is **marketing** or **transactional**. This affects opt-outs, frequency limits, and whether the message is sent to users in the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-control-group-membership-works). [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#choosing-the-right-message-type). For SMS and Viber, marketing consent also decides whether a marketing message reaches a phone number. [Learn how SMS and Viber marketing consent works](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/). --- # Getting started with emails **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/ Learn how to create, send, and track email campaigns with Pushwoosh to boost engagement, conversions, and retention. --- Email is an effective communication channel available on the Pushwoosh platform. You can use email independently or combined with other channels like push notifications, in-app messages, SMS, or WhatsApp. ## Why use emails in your campaigns Here are some of the examples of how you can use emails for your business goals. * **Onboarding & engagement**: Welcome new users, highlight product value, and drive first purchases. * **Re-engagement**: Reconnect with inactive users, including those who’ve opted out of push, by sending targeted offers and updates. * **Product launches**: Announce new products, showcase key features, and gather customer feedback through follow-up push and in-app messages. ## Prerequisites for sending emails Developer assistance needed You might need assistance from your developer team for these steps. Share the links below with them to get started. Before you can send email messages, you must complete the following steps: 1. **Configure your email platform** Verify your email and domain to ensure email deliverability. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) 2. **Add email subscribers**\ [Collect addresses with subscription forms](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) on your website, [import an existing email list](https://docs.pushwoosh.com/product/messaging-channels/emails/get-email-subscribers#import-email-list), or [register subscribers via API](https://docs.pushwoosh.com/developer/first-steps/subscribe-users#email). 3. **Set up the subscription preference center**\ Configure the subscription preference center so users choose which email categories they receive. Only opted-in users get emails for each category. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/) 4. **Follow deliverability best practices** Keep your email list clean, use double opt-in, and avoid spam-like content to protect your sender reputation. Before you send a one-time email, run a pre-send audit to catch deliverability and compliance issues. [Learn more about email deliverability](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/) and [how to audit your email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email). ## Set up your email Before designing your email, ensure you configure your email settings, define languages, personalize subject lines, etc. [Learn how to set up your email ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/) ## Create and customize email content Pushwoosh provides multiple tools for designing and personalizing your emails: * **Drag & drop editor**: Create visually appealing emails without coding. [Learn more about the Drag & drop editor ](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/) * **Use AI tools in the Drag & drop editor**: Speed up content creation with a chat-based AI assistant, AI-generated images and alt text, subject/preheader drafting, and auto-translate. [Learn more about email AI tools ](https://docs.pushwoosh.com/product/content/email-content/ai-tools-in-email-editor/) * **HTML code editor**: Customize your emails with full control over the design. [Learn more about the HTML code editor ](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/) * **Reuse email content blocks across campaigns**: Insert saved content blocks (e.g., headers, footers) into new emails using content IDs. [Reuse email content blocks ](https://docs.pushwoosh.com/product/content/email-content/reuse-email-content-blocks/) * **Pre-built email templates**: Use ready-made designs to save time. [Browse email templates ](https://docs.pushwoosh.com/product/content/email-content/email-templates-library/) ### Personalize emails for better engagement Make your emails more relevant, increasing open rates and conversions. [Learn more about email personalization ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-email/) ## Sending emails Send emails through the one-time email form or automate delivery with Customer Journey. For one-time emails, run a pre-send audit in the wizard before launch. [Learn more about sending emails ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/) ## Analyze email performance Pushwoosh provides detailed analytics to track email engagement and optimize your campaigns: * **Email statistics** Monitor open rates, click rates, and conversions. [Learn more about email statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) * **Customer Journey insights** Evaluate email performance alongside push notifications, in-app messages, and SMS. [Learn more about Customer Journey statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#communications/) --- # Email deliverability **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/ Learn how to improve email deliverability with Pushwoosh through validation, bounce handling, double opt-in, and pre-send audit. --- Email deliverability is the ability of your emails to reach users’ inboxes instead of being marked as spam or blocked. High deliverability is vital for user engagement. This post outlines key email deliverability best practices and explains how Pushwoosh supports them with real-time validation, bounce management, double opt-in, sunset flow automation, and a pre-send email audit. ## Email deliverability best practices To maintain high deliverability and protect your sender reputation, follow these universal best practices: ### Keep your email list clean * Remove invalid, inactive, or misspelled addresses (e.g., @gnail.com). * Eliminate disposable or temporary emails. Note Pushwoosh automatically handles invalid and disposable addresses during registration, helping you maintain a clean list with minimal effort. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#email-validation) ### Use explicit (double) opt-ins Double opt-in is a two-step process for collecting email consent. After a user submits their email address (e.g., via a form or app registration), they receive a confirmation email with a link. Only after clicking this link is the user added to your mailing list. **Why it is important** * Improves deliverability by ensuring only real, interested users are subscribed * Reduces spam complaints and protects sender reputation * Increases engagement quality by filtering out fake or low-intent signups * Supports legal compliance with data protection and anti-spam regulations Tip Use [Pushwoosh Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) to automate the double opt-in process. [Learn more](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-create-a-double-opt-in-campaign/) ### Monitor engagement and performance Track how your audience interacts with your emails to spot and fix potential deliverability issues early. Focus on key metrics like opens, clicks, bounces, and spam complaints. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) Note Pushwoosh helps you manage this automatically and hard-bounced emails are tagged and excluded from future sends. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#bounce-management) ### Avoid spam-like characteristics in your content To prevent your emails from being flagged as spam: * Avoid excessive use of promotional language (e.g., “Buy now!”, “Free!” in subject lines) * Do not use deceptive subject lines or misleading headers * Limit the use of all caps and multiple exclamation points * Keep a healthy text-to-image ratio * Ensure that the content is relevant and expected by the recipient ### Avoid common pitfalls * Do not purchase email lists. * Avoid sudden spikes in sending volume. * Use consistent sender names and addresses. ### Identify and remove disengaged subscribers Users who don’t engage with your emails, even if their addresses are valid, can still hurt your deliverability. To keep your list healthy, identify inactive users and ask them to confirm whether they still want to receive your emails. This process is called a **sunset email flow**. A sunset flow helps you: * Clean your list by removing users who no longer engage * Improve open and click rates * Lower the chance of being marked as spam * Protect your sender reputation #### Stop marketing emails to inactive contacts The sunset policy stops marketing emails to contacts who haven’t opened or clicked your emails for a period you choose. You set it for each project, and it’s off by default. Transactional emails ignore it. To turn the policy on and set the period, see [Turn on the sunset policy](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#turn-on-the-sunset-policy). #### How the sunset policy works Pushwoosh checks each contact when a marketing email is sent and skips the contacts who are inactive. ##### Who counts as inactive A contact is inactive if their last open or click, depending on what counts as activity, is older than the period you set. For a contact who has never opened or clicked, the period starts from the date the contact was added. ##### What happens to a skipped contact An inactive contact is only skipped. Pushwoosh doesn’t unsubscribe the contact or change their tags. The contact gets marketing emails again in either of these cases: * You turn the policy off. * The contact opens or clicks an email you send with the policy ignored. ##### Which emails the policy applies to The policy applies to marketing emails sent as one-time messages, from journeys, and through the API. ##### Send to inactive contacts too To send a marketing email to inactive contacts too, turn on **Ignore sunset policy** in the [one-time email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#ignore-the-sunset-policy) or in the journey [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#ignore-the-sunset-policy). The switch appears only when the policy is on and the email is a marketing message. ##### Skipped contacts in statistics Skipped contacts appear in the email funnel as **Excluded: sunset policy** and aren’t counted in **Sent** or **Delivered**. See [Email statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#what-each-step-includes). #### Build a sunset flow in a journey The sunset policy stops emails to inactive contacts. To ask them first whether they still want your emails, build a sunset flow in a journey. Tip Use [Pushwoosh Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) to build an automated sunset flow that reconfirms user interest and cleans your email list. [Learn more](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow/) ## How Pushwoosh helps you maintain deliverability Pushwoosh includes several built-in mechanisms to automatically protect your email reputation and keep your list clean. ### Email validation When users register their email addresses, Pushwoosh automatically blocks malformed entries (e.g., missing “@” or domain) and rejects disposable or temporary email addresses. This validation helps reduce bounces, avoid wasted sends, and protect your sender reputation. The same checks apply to addresses you [import from a CSV file](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/#rows-skipped-during-import), together with the bounce list check described below. After a CSV import, Pushwoosh also [unsubscribes addresses on domains without a mail server](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/#email-addresses-on-domains-without-a-mail-server). ### Bounce management Pushwoosh automatically checks email addresses against its internal bounce list and blocks re-registration of addresses marked as bounced. This helps maintain a clean email list and protects your campaign performance. **How it works** ![Flowchart showing bounce management process from delivery attempt through classification to bounce list exclusion](https://docs.pushwoosh.com/emails-email-deliverability-1.webp) 1. Pushwoosh monitors every email delivery attempt. 2. If a message bounces, the system captures the response and classifies it as a hard or soft bounce. 3. In case of a hard bounce, the email address is added to an internal bounce list and excluded from your subscriber list. 4. If a user later tries to register with an email address already on the bounce list, Pushwoosh will automatically prevent it from being added to your email audience, maintaining the integrity of your contact list. If you’ve fixed the issue that got an address suppressed, for example a corrected mailbox or a subscriber who reconfirms consent after a spam complaint, you can manually reactivate it from the user’s profile or from the account-wide [Email suppression list](https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/). [Learn how to reactivate a suppressed email address](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#reactivate-a-suppressed-email-address). #### Bounce spike warning before email sending is paused Pushwoosh measures bounce rates over a limit period set by your plan, 14 days by default. If a rate over that period exceeds its limit, Pushwoosh [pauses email sending](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/#sending-suspension) in the project that received the bounces, including all its messages and Journeys. To give you time to react, Pushwoosh warns you about a sharp rise in bounces before that happens. Pushwoosh checks the emails sent from your account in the last 24 hours. You get a warning when at least 1,000 emails were sent and either of the following is true: * Hard bounces reach half of your plan’s hard bounce limit, for example 2.5% with a 5% limit. * Soft bounces reach 45% of emails sent, which is half of the 90% soft bounce limit. Complaints and spam-related bounces don’t trigger this warning, so sending can be paused for these reasons without a warning first. The warning appears in the [Notification center](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#notification-center-top-right-corner), and the account owner may also receive it by email. It shows: * The hard and soft bounce rates for the last 24 hours. * How close your bounce rates over the limit period are to their limits. * A note that sending will be paused once a limit is exceeded, if those rates are already at half of a limit or more. Pushwoosh sends at most one warning every 72 hours. When you get the warning: 1. Open [Email statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) and find the recent sends with the most bounces. 2. Remove outdated, invalid, and misspelled addresses from the audience of those sends, especially recently imported lists. 3. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you can’t find the cause of the bounces. ### Double opt-in automation Pushwoosh enables you to create automated double opt-in flows using [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). This helps you verify new subscribers’ intent and maintain list quality. [Learn how to create a double opt-in campaign ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-create-a-double-opt-in-campaign/) ### Sunset email flow automation Pushwoosh also supports automated **sunset email flows** to identify disengaged users and prompt them to confirm their interest. This ensures your list remains active and reduces the risk of spam complaints. [Learn how to set up a sunset flow ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow/) ### Pre-send email audit Before you send a one-time email, run an audit in the campaign wizard. Pushwoosh sends a test message and checks content and compliance issues such as broken links, spam score, unsubscribe headers, and a visible unsubscribe link. [Learn how to audit a one-time email ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email/) ### List-Unsubscribe header support To provide a seamless opt-out experience, Pushwoosh supports the List-Unsubscribe header in emails. When enabled, this header allows mailbox providers like Gmail and Yahoo to display a native Unsubscribe button directly in the email inbox interface, making it easier for recipients to opt out without searching for a link in the message body. Using the List-Unsubscribe header helps improve deliverability by reducing spam complaints, meeting mailbox provider requirements, and protecting your sender reputation. **How to use** Pushwoosh automatically includes a default unsubscribe link in the List-Unsubscribe header for all emails. Tip To customize unsubscribe behavior, for example, to let users unsubscribe from a specific category, you can create your own unsubscribe form and logic. Then, include the link to that form as a custom unsubscribe URL by passing it in the `list_unsubscribe` parameter when sending emails via the [`createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage) API. This URL will be added to the **List-Unsubscribe** header. --- # Email suppression list **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/ View, search, and reactivate every suppressed email address in your account, then export the filtered list as a CSV file --- The **Email suppression list** shows every email address Pushwoosh stopped mailing after a spam complaint or a hard bounce, across your whole account. Reactivating an address here takes it off the list for every project, not just the one you opened it from. To open the list, go to **Settings**, then select **Email suppression list**. Opening the list requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. Note This is the same suppression data shown on a subscriber’s own profile. See [Reactivate a suppressed email address](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#reactivate-a-suppressed-email-address) to reactivate one address at a time from there instead. ## Filter and search the list Use the filters above the table to narrow the list: * **Search by address:** an exact match on one email address, not a partial search. * **Source:** **Any source**, **Spam complaint**, or **Hard bounce**. * **Reason:** **Any reason** or one bounce reason, such as **Mailbox full**. Reasons apply to hard bounces only, so this filter is unavailable when **Source** is set to **Spam complaint**. * **From** and **To:** limit results to a date range. Click **Apply** to run the filters, or **Reset** to clear them. ## Review suppressed addresses The table lists matching addresses, newest first, with these columns: * **Email:** the suppressed address. * **Source:** **Spam complaint** or **Hard bounce**. * **Date:** when the suppression occurred. * **Reason:** what caused the suppression. * A hard bounce shows the bounce reason, such as **Address doesn’t exist**, followed by the SMTP code the recipient’s mail server returned, such as `5.1.1`. See [Bounces by reason](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#bounces-by-reason) for what to do about each reason. * A spam complaint, or a hard bounce with no reason assigned, shows a human-readable summary of the provider’s feedback. * Long text is truncated in the table. Hover over the reason to read the full provider response. * **Recorded in:** the project the suppression was recorded in. The list is paginated at 50 addresses per page. The total count of suppressed addresses appears below the table. ![Email suppression list with Search by address, Source, and Reason filters and a table of suppressed addresses showing bounce reasons with SMTP codes](https://docs.pushwoosh.com/emails-email-suppression-list-1.webp) ## Reactivate an address from the list Note Only the account owner can reactivate a suppressed address. If someone else needs to reactivate an address, contact Pushwoosh support. 1. Click **Reactivate** in the address’s row. 2. If the address was suppressed for a spam complaint, check the box confirming the recipient has renewed their consent. This is required for spam complaints, and the reactivation can’t proceed without it. A hard bounce reactivation skips this step. 3. Optionally, add a **Comment** (up to 255 characters). 4. Click **Reactivate**. ![Reactivate email address dialog with a Comment field, Cancel button, and Reactivate button](https://docs.pushwoosh.com/emails-email-suppression-list-2.webp) This is the same reactivation as on a subscriber’s profile: it clears the [**Bounced Email**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) tag, re-registers the email device if a complaint had removed it, and is logged internally so support can look up who reactivated an address and when. See [Reactivate a suppressed email address](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#reactivate-a-suppressed-email-address) for these details. ## Export the list as a CSV file 1. Filter the list to the addresses you want to export. 2. Click **Export CSV** at the top of the page. The file contains the `date`, `email`, `source`, `reason`, `recorded_in`, `bounce_category`, and `smtp_code` columns. For spam complaints, `bounce_category` and `smtp_code` are empty. Pushwoosh prepares the file in the background. A banner appears at the top of the page while the export is in progress, and a **Download CSV** button appears in it once the file is ready. ![Email suppression list page with an Exporting complete banner and a Download CSV button](https://docs.pushwoosh.com/emails-email-suppression-list-3.webp) Note The **Export CSV** button is disabled while an export is already running, and when the current filters return no addresses. --- # Get email subscribers **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/get-email-subscribers/ Collect email addresses and import email lists to start email marketing with Pushwoosh and engage your audience effectively. --- To effectively benefit from email marketing, the first step is building a strong email audience. Collecting email addresses and importing existing contacts are crucial to start engaging your users and sending targeted campaigns. Note To maintain high deliverability and protect your sender reputation, ensure your email list is clean and valid. Avoid importing temporary or misspelled addresses, and use confirmed opt-in where possible.\ [Learn more about email deliverability best practices and how Pushwoosh helps you](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/) ## Collect email addresses Think about where and when it makes sense to ask users for their email addresses. For example, you might ask for an email: * On your website with **subscription forms**. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) * On the login or registration page of your app. * When users sign up for a newsletter or special offer. * After a purchase is completed. Developer assistance needed Once you’ve decided where to collect emails, you’ll need help from your development team to register these emails for your app and associate them with user devices. Please share [this guide](https://docs.pushwoosh.com/developer/first-steps/subscribe-users#email) with detailed instructions with your developer. ## How to import your email list from CSV Use the shared [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) wizard to import your email list from a CSV file. The wizard can also register SMS, WhatsApp, and Viber subscribers from the same file, so map only the column with email addresses to the **Email** channel to register those contacts as email subscribers. Note Each new CSV you upload adds to the contacts you already imported instead of replacing them. If the file includes addresses that are already registered, only the new addresses are added. The already-registered ones stay as they are. With your email contacts imported, set up the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) so you can assign categories when sending and let users manage their preferences. Then create email content using the [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) or [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor) and [send an email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails). --- # Preference center overview **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/ Let subscribers choose which email categories they receive. Learn what the preference center is, why to set it up, and where to configure categories and subscription pages. --- The email subscription preference center lets subscribers choose which types of emails they want to receive, such as newsletters, promotions, or product updates. Instead of unsubscribing from all emails, users can opt out of specific categories and stay subscribed to what matters most to them. ## Why you should set it up A preference center improves the user experience and helps you meet legal requirements: * **Better user experience.** Users choose which types of emails they receive, such as newsletters, promotions, or product updates. This reduces full opt-outs and keeps your audience more engaged. * **Compliance with email regulations.** Several laws require that users can easily manage their communication preferences: * **GDPR** (EU) requires clear options to opt in or out of specific types of communication. * **CAN-SPAM** (U.S.) requires every commercial email to include a visible unsubscribe link. Without these options, you may face legal penalties, and your email service provider (ESP) account may be flagged or suspended. ## Start working with the preference center Important Email categories apply only to campaigns created **after** the feature is enabled. Campaigns created before category setup ignore user preferences entirely. 1. **Set up categories** Create the email categories users can choose from, such as *Newsletter* or *Product Updates*, and decide who is subscribed automatically. When you save, the preference center turns on and the subscription pages are ready. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#set-up-categories) 2. **Add links to your emails** Add two links to your emails: an unsubscribe link and a link to the preference center. Users reach the subscription pages through these links. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#add-links-to-your-emails) 3. **Choose a category for each campaign** Choose a category for every marketing email. Only users subscribed to this category receive the email. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#select-a-subscription-category-in-your-campaigns) 4. **Check what users see** Check the subscription pages users land on after a click. They are ready-made and work by default, and you can customize them later. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#what-happens-when-users-click-your-links) ## Manage categories Add, rename, and delete categories, or turn the preference center off and on. [Learn more about managing categories ](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#manage-the-preference-center/) ## Manage unsubscribes Choose the steps users go through after they click the unsubscribe link, and collect the reasons for leaving. [Learn more about managing unsubscribes ](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/) ## Customize subscription pages Change the text, design, and buttons of the pages users see when they unsubscribe or manage preferences. [Learn more about customizing subscription pages ](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/) ## How the preference center works Pushwoosh stores each user’s category subscriptions in a tag and uses them to decide who receives which email. ### Categories and subscriptions Pushwoosh tracks category subscriptions using the **`Subscribed Email Categories`** tag. This tag stores the list of categories each subscriber is currently subscribed to. Delivery logic: * If a category name is in the list, the subscriber receives emails for that category. * If a category name is not in the list, the subscriber does not receive emails for that category. * You can populate this tag automatically when [adding a category](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#add-a-category) or by [importing subscription categories via CSV](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#import-subscription-categories-via-csv). Deprecated tag The previously used **`Unsubscribed Email Categories`** tag is deprecated and will be supported only until **May 31, 2026**. Use **`Subscribed Email Categories`** for all new setups and updates. ### How categories apply to campaigns Once the preference center is set up, every marketing campaign you send (via Journey or One‑Time Message) will prompt you to assign a category before sending. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#select-subscription-category) This ensures emails are only delivered to subscribers who are subscribed to that content type. ### Handling transactional messages Transactional messages are a separate message type designed for critical, non-promotional communication. Unlike marketing emails, transactional messages bypass all delivery restrictions to ensure they reach the user. When creating an email campaign in Pushwoosh, you must choose between two message types: marketing and transactional. [Learn more about message types and delivery rules](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ### Full opt-out with the Unsubscribed Emails tag For a complete opt-out from all marketing emails, Pushwoosh sets the [**Unsubscribed Emails**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) tag to `true`. This happens when a subscriber: **With the preference center enabled:** * Clicks **Unsubscribe from all** in the preference center. **Without the preference center:** * Clicks the unsubscribe link in any email and, if the **Confirmation** step is on, confirms. This opts them out of all marketing emails. In both cases, unsubscribing via the email client using the [List-Unsubscribe header](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#list-unsubscribe-header-support) (for example, the native unsubscribe button in Gmail or Apple Mail) also sets the tag to `true`. [Transactional emails](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) are not affected by this tag and are always delivered. ### Events and segments Every subscribe, unsubscribe, and resubscribe action also fires the [**PW_EmailSubscribed**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailsubscribed) or [**PW_EmailUnsubscribed**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emailunsubscribed) event, so you can trigger follow-up automation based on category-level opt-ins and opt-outs. #### Segment by category To target users by a specific email category: 1. In segment builder, click **Event** in **Add filter by**, then select **PW_EmailSubscribed** or **PW_EmailUnsubscribed**. 2. Click **+ Attribute** and select **categories**. 3. Choose an operator. See [event attribute operators by type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#event-attribute-operators-by-type). 4. Pick a category from the value dropdown. It lists your actual email categories instead of raw recorded values. ## FAQ Answers to common questions about categories, transactional emails, unsubscribe links, and the survey. [Read the preference center FAQ ](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/preference-center-faq/) --- # Customize subscription pages **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/ Edit the subscription pages users see when they unsubscribe or manage email preferences, including text, blocks, buttons, languages, and the survey block. --- Subscription pages are the pages users see when they unsubscribe from an email or manage their email preferences. Edit them in the page builder. To see when users get each page, read [What happens when users click your links](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#what-happens-when-users-click-your-links). To choose which pages users see after an unsubscribe, see [Manage unsubscribes](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/). ## Edit a page Edit a subscription page in the page builder to change its text, design, and buttons. Users see their categories on the **Preference center** page only while the preference center is on. To turn it on, [set up categories](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#set-up-categories). To show the **Confirmation** or **Survey** page, turn its step on in the [unsubscribe flow](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#set-up-the-unsubscribe-flow). 1. Go to **Settings > Preference Center**, switch to the **Subscription pages** tab, and click a page tile. ![Subscription pages tab with seven page tiles, a Disabled badge on the Survey tile, and the Unsubscribe flow button](https://docs.pushwoosh.com/emails-email-preferences-21.webp) 2. Edit the page: * Change the text right on the canvas. * Add blocks from the **Blocks** tab. * Select a block to adjust it in the settings panel. 3. To check how the page looks on desktop and mobile, open the three-dot menu at the top and select **Preview**. ![Three-dot menu at the top of the page builder with the Preview option highlighted](https://docs.pushwoosh.com/emails-email-preferences-23.webp) 4. Click **Save**. Until you save a page, users see the standard Pushwoosh design. You don’t publish or connect anything: the saved version replaces the standard one for that step, and the changes go live for all users as soon as you save. Personalize the page text Use these placeholders in the page text: * `%%PW_SUBSCRIBER_EMAIL%%` for the user’s email address. * `%%PW_EMAIL_CATEGORY%%` for the name of the category the user is unsubscribing from or resubscribing to. It’s empty when the link isn’t tied to a category. The builder shows the placeholders as typed. Pushwoosh replaces them with real values when a user opens the page. ### Add a language Each page can have several language versions. To add one, click **+** next to **Languages**. Users see the version in their browser language. If there is no such version, they see the default language, then English. ## Page buttons A button on a subscription page can open a website or perform a subscription action. To set the action: 1. Select the button on the canvas. 2. In the link settings, choose the action. Text links can only open a website. For example, give a button on the **Unsubscribed from category** page the **Resubscribe** action to let users opt back into the category. ### Actions by page The actions you can choose in the link settings depend on the page. The table splits them into two groups: * **Required actions:** add a button with each of them, or the builder won’t save the page. This way users can always complete the unsubscribe. * **Optional actions:** add them if you need them. | Page | Required actions | Optional actions | | ----------------------------------------------------- | ---------------------------------------------- | -------------------------------------------------------- | | **Preference center** | **Save preferences**, **Unsubscribe from all** | — | | **Confirmation** | **Confirm unsubscribe** | **Manage preferences** | | **Survey** | **Send survey answer** | **Skip survey**, **Manage preferences**, **Resubscribe** | | **Unsubscribed from category** | **Resubscribe** | **Manage preferences**, **Unsubscribe from all** | | **Unsubscribed from all** | **Resubscribe** | **Manage preferences** | | **Resubscribed to category**, **Resubscribed to all** | — | **Manage preferences** | A **Manage preferences** button is hidden from users while the preference center is disabled. ## Blocks Two blocks have their own settings: the **Categories** block on the **Preference center** page and the **Survey** block on the **Survey** page. ### Categories block The **Preference center** subscription page must contain one **Categories** block. It lists your email categories with a checkbox for each, so users can choose what they receive. To adjust it: 1. Create at least one category. Without categories, the block shows a **No categories yet** placeholder and its settings show only a **No categories created** warning. Once categories exist, the canvas and the preview show them. 2. Select the block on the canvas. 3. Change the **Checkbox color**, or edit the **Status messages** users see **After a successful save** and **When the save fails**. ### Survey block The **Survey** subscription page must contain one **Survey** block. Users pick one answer option, and the answer goes to the **`Unsubscribe Reason`** tag, listed under **Tag to save answers**. To set up the block: 1. Select the block on the canvas or in **Layers** to open its settings. 2. Under **Answers**, edit, reorder, add, or remove **Answer options**. ![Survey block settings with the Unsubscribe Reason tag and the list of answer options](https://docs.pushwoosh.com/emails-email-preferences-24.webp) 3. To let users type their own reason, turn on **Add “Other” option with a text field**, then set its **Option name** and **Placeholder**. 4. To let users leave without answering, select **Add “Skip” button**. ![Survey block settings with the Other option switch, Option name and Placeholder fields, and the Add Skip button checkbox](https://docs.pushwoosh.com/emails-email-preferences-25.webp) 5. Under **Appearance**, change the **Checkbox color** and the spacing and font size of the options. The survey needs at least one answer option or the **Other** field. --- # Preference center FAQ **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/preference-center-faq/ Answers to common questions about email categories, renaming and deleting categories, transactional emails, unsubscribe links, and why users unsubscribe. --- Answers to common questions about the preference center. To set it up, see [Set up the preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). ## What happens if I rename a category? When you rename a category, the values in **`Subscribed Email Categories`** are automatically updated to match the new category name for all subscribers. ## What happens to existing campaigns when I set up the subscription preference center? Email categories only apply to new campaigns and templates created after the feature is enabled. Campaigns created before category setup will be sent without regard to user preferences and will bypass opt-outs (like transactional messages). ## Will users still receive transactional emails if they unsubscribe from all categories? Yes. Transactional messages bypass user preferences, unsubscribe settings, and frequency capping to ensure critical communication, such as password resets or order confirmations, is always delivered. ## What happens to campaigns if I delete a category used in them? Any active campaigns or journeys that use the deleted category will be paused automatically. You’ll receive a notification in the **Notification Center** informing you about the issue. To resume sending, you must assign a new category to the affected campaigns or journeys. ## Can I create custom category names? Yes. You can create up to 20 unique categories per project. Use clear, user-friendly names that match the type of content users expect to receive (e.g., *Newsletters*, *Product Updates*). ## How do I find out why users unsubscribe? Turn on **Unsubscribe reason survey** in the [unsubscribe flow](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#set-up-the-unsubscribe-flow). Answers are saved to the **`Unsubscribe Reason`** tag. To see them, click **View stats** on the **Survey** tile. ## Do I need to do anything to enable unsubscribe links? Add an [unsubscribe link](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#unsubscribe-link) to your email content so recipients can opt out. The pages users see after clicking it are described in [What happens when a user clicks the unsubscribe link](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#what-happens-when-a-user-clicks-the-unsubscribe-link). Optionally, add a [Preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#preference-center-link) link so users can manage all categories in one place. --- # Set up the preference center **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/ Configure the subscription preference center so users can choose which email categories they receive. Add categories, manage opt-ins, and meet compliance requirements. --- Set up email categories so users can choose which emails they receive. For what the preference center is and why to use it, see the [overview](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/). ## Setup steps Follow these steps to turn on the preference center and start using email categories. Important Email categories apply only to campaigns created **after** the feature is enabled. Campaigns created before category setup ignore user preferences entirely. ### Set up categories To enable category-based filtering and let users manage preferences, set up categories in your project: 1. Go to **Settings > Preference Center**. 2. Click **Set up categories** to define which email categories users can subscribe to (e.g., *Promotional*, *Newsletter*, *Product Updates*, *Events*). ![Categories tab of the Preference center page with the Set up categories button highlighted](https://docs.pushwoosh.com/emails-email-preferences-16.webp) Tip You can define up to 20 categories per project. 3. In the **Set up categories** modal, review the suggested category list, edit category names if needed, and add new categories with **Add category**. For each category, configure subscription options: * **Subscribe new** to subscribe new users automatically when they register. * **Existing users** to subscribe existing users once when you save changes. To subscribe users later or in bulk, [import a CSV file](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#import-subscription-categories-via-csv). Category naming * Use names your recipients will understand and avoid internal or technical jargon. * Each category name must be unique. 4. Click **Save**. Users will see these categories when they open the preference center. ![Set up categories modal with category names and Subscribe new and Existing users columns](https://docs.pushwoosh.com/emails-email-preferences-17.webp) After you save, the subscription pages are ready. See [What happens when users click your links](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/#what-happens-when-users-click-your-links). ### Add links to your emails Users manage the categories you just set up through links in your emails. Add two links: an unsubscribe link, so users can opt out, and a preference center link, so users can choose which categories they receive. #### Unsubscribe link Add the [unsubscribe link](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#unsubscribe-link) to your email content. After a click, users go through the [unsubscribe flow](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/). #### Preference center link Insert a link to the preference center in your emails (for example, in the footer or a call-to-action button) so users can manage their communication preferences. To do so: * In the Drag & drop editor, select **Manage preferences link** from the **Action type** dropdown. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#manage-preferences-link) * In the HTML code editor, use the variable `%%PW_EMAIL_PREFERENCE_CENTER_LINK%%`. [Learn more](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/#preference-center-link) * To copy the placeholder, open the three-dot menu in the top right of the **Subscription categories** section on the **Categories** tab and select **Copy link to Preference center**. Paste it into an email. ### Select a subscription category in your campaigns When you create a marketing email, select a **Subscription category** in the campaign form or in the Journey email step. Only users subscribed to this category receive the email. Learn how in [one-time emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#select-subscription-category) and [Customer Journey emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-emails-via-customer-journey/#select-subscription-category). ### What happens when users click your links Users land on ready-made subscription pages. The pages work by default, so you don’t need to connect them. Which page a user sees depends on what they click: the unsubscribe link and the preference center link lead to different pages, and each page leads to the next one through its buttons. There are seven pages, and users see each one at a different moment: | Page | When users see it | | ------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Preference center** | They open the preference center link or click **Manage preferences**. They choose which types of emails to receive and click **Save preferences**, unsubscribe from several categories at once, or click **Unsubscribe from all** to opt out of all marketing emails. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#full-opt-out-with-the-unsubscribed-emails-tag) | | **Confirmation** | They click the unsubscribe link, and **Confirm before unsubscribing** is on. | | **Survey** | They have just unsubscribed, and **Unsubscribe reason survey** is on. | | **Unsubscribed from category** | They unsubscribed from the category of the email. This is the last page of the unsubscribe flow. | | **Resubscribed to category** | They clicked **Resubscribe** after unsubscribing from a category. | | **Unsubscribed from all** | They clicked **Unsubscribe from all**. | | **Resubscribed to all** | They clicked **Resubscribe** after unsubscribing from all emails. | To see the steps after the unsubscribe link in detail, read [What happens when a user clicks the unsubscribe link](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#what-happens-when-a-user-clicks-the-unsubscribe-link). ![Subscription pages tab with seven page tiles, a Disabled badge on the Survey tile, and the Unsubscribe flow button](https://docs.pushwoosh.com/emails-email-preferences-21.webp) Change the pages To change the text, design, or buttons of any of these pages, see [Customize subscription pages](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/). To change the steps after the unsubscribe link, see [Manage unsubscribes](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/). ## Manage the preference center Use the **Categories** tab to add, edit, and delete categories and to check how many users are subscribed to each. In the table, **Subscribed users** is the number of users currently subscribed to a category. **Auto-subscribe** is **Enabled** when new users are subscribed to the category automatically. ![Categories tab with the Subscription categories list in Enabled state, Auto-subscribe and Subscribed users columns, and Add category button](https://docs.pushwoosh.com/emails-email-preferences-15.webp) ### Add a category To add a category to your preference center: 1. Click **Add category**. 2. Enter a clear, unique category name. 3. Select **Auto-subscribe new users** to subscribe new users to this category automatically when they register. 4. Select **Subscribe existing users** to subscribe current users to this category when you save it. 5. Click **Add**. ![Add category modal with Category name field and options to auto-subscribe new and existing users](https://docs.pushwoosh.com/emails-email-preferences-18.webp) Note For large audiences, applying **Subscribe existing users** may take some time. ### Rename a category 1. To rename a category, open the three-dot menu next to the category and click **Edit settings**. 2. In **Category settings**, update the category name and set the **Auto-subscribe new users** checkbox as needed: select it to enable auto-subscription, or clear it to disable. 3. Click **Save**. ![Category settings modal with Category name field, Auto-subscribe new users checkbox, and Save button](https://docs.pushwoosh.com/emails-email-preferences-19.webp) Once the category is renamed, the values in **`Subscribed Email Categories`** are automatically updated to match the new category name for all subscribers. ### Delete a category To delete a category, open the three‑dot menu next to it and select **Delete category**. What happens when you delete a category * The category is permanently removed from the preference center. * Users who had this category in their preferences will no longer see it. * Any active campaigns or journeys using the category will be paused automatically. You’ll receive a notification in the [**Notification Center**](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#notification-center-sidebar) informing you about the issue. To resume sending, reassign a new category in any affected campaigns. ### Import subscription categories via CSV Use **Import subscription categories** to import user subscriptions and bulk update the **`Subscribed Email Categories`** tag from a CSV file. You can open this flow in either of these ways: 1. On the **Categories** tab, open the three-dot menu and select **Subscribe users manually**. ![Import CSV page with Import subscription categories tile for uploading category subscriptions from CSV](https://docs.pushwoosh.com/emails-email-preferences-12.webp) Alternatively, in the main menu, go to **Audience** > **Import CSV** > **Import subscription categories**. 2. Upload your CSV file. ![Import subscription categories screen with Upload CSV section and file validation before import start](https://docs.pushwoosh.com/emails-email-preferences-13.webp) 3. Click **Start import**. 4. Review results. CSV requirements * The first column must contain either [`HWID`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/#create-csv) or [`User ID`](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/#what-is-a-user-id). * The second column must be **`Subscribed Email Categories`**. * Category names in **`Subscribed Email Categories`** must be comma-separated. * Save the file in `UTF-8` encoding. * Use a comma as the CSV delimiter. * If an identifier in the first column is not registered in the selected project, that row will be skipped. * The form validates the file automatically before upload. ### Disable and enable the preference center To disable the preference center: 1. On the **Categories** tab, click the **three-dot menu** in the top right of the **Subscription categories** section. 2. Select **Disable Preference center**. ![Three-dot menu of the Subscription categories section with the Disable Preference center option highlighted](https://docs.pushwoosh.com/emails-email-preferences-20.webp) 3. In the confirmation dialog, click **Disable**. What happens when you disable the preference center * Subscribers will no longer see category options in the preference center. * The **Preference center** page is disabled until you enable it again. * The unsubscribe link still works and opts users out of all marketing emails, not one category. Users still go through the **Confirmation** and **Survey** steps if they are on in the [unsubscribe flow](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/). * Emails will continue to be sent as usual, but without category-based filtering, until you re-enable the preference center. To turn it back on, select **Enable Preference center** in the same menu. --- # Manage unsubscribes **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/ Choose what users see after they click the unsubscribe link in an email, including a confirmation, an optional survey about the reason for leaving, and a final page. Collect the reasons in a tag. --- Users go through the unsubscribe flow after they click the unsubscribe link in an email. Add the link in the [drag and drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#unsubscribe-link) or the [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/), then choose which steps users see. ## Set up the unsubscribe flow The unsubscribe flow defines which steps users go through after clicking the unsubscribe link. Its two optional steps are the **Confirmation** and **Survey** pages. 1. Go to **Settings > Preference Center** and switch to the **Subscription pages** tab. 2. Click **Unsubscribe flow**. 3. In **Edit unsubscribe flow**, turn the steps on or off: * **Confirm before unsubscribing:** shows the **Confirmation** page before the unsubscribe is applied. On by default. * **Unsubscribe reason survey:** shows the **Survey** page after the unsubscribe is applied. Off by default. 4. Click **Apply**. ![Edit unsubscribe flow modal with Confirm before unsubscribing and Unsubscribe reason survey switches](https://docs.pushwoosh.com/emails-email-preferences-22.webp) After you apply the flow: * To change the text or design of the **Confirmation** or **Survey** page, see [Customize subscription pages](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/#edit-a-page). * To see what users go through at each step, read [What happens when a user clicks the unsubscribe link](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#what-happens-when-a-user-clicks-the-unsubscribe-link). ## What happens when a user clicks the unsubscribe link When a user clicks the [**Unsubscribe link**](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#unsubscribe-link) in an email, they are unsubscribed only from the category assigned to that email (e.g., *Newsletter*). The pages they see depend on your [unsubscribe flow](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/#set-up-the-unsubscribe-flow): 1. **Confirmation** (on by default). The user confirms that they want to unsubscribe. Nothing changes until they click the confirm button. 2. **Survey** (off by default). After the unsubscribe is applied, the user can tell you why they left. The answer is saved to the **`Unsubscribe Reason`** tag. 3. **Unsubscribed from category**. The final page confirms they will no longer receive emails from that category. ![Unsubscribed from category page with Resubscribe and Manage preferences buttons](https://docs.pushwoosh.com/emails-email-preferences-27.webp) On the final page, users can click: * **Resubscribe** to opt back into that category. They see the **Resubscribed to category** page. * **Manage preferences** to open the preference center and adjust all their email preferences. The default flow works automatically, and no additional setup is required. To change the text, design, or buttons of any of these pages, see [Customize subscription pages](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/). If the preference center is disabled, the unsubscribe link opts users out of all marketing emails, not one category. The **Confirmation** and **Survey** steps still follow your unsubscribe flow. Note If the unsubscribe link has a redirect URL, users skip these pages. They are unsubscribed right after the click and taken to your website. ## Collect unsubscribe reasons Turn on the survey to learn why users unsubscribe. To edit the survey page, see the [Survey block](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/customize-subscription-pages/#survey-block). When you turn on the survey, Pushwoosh creates the **`Unsubscribe Reason`** string tag in your account. Each answer is written to this tag on the user’s email profile: * If the user picks an option, the tag stores the option text in the page’s default language, even if the user saw a translated page. * If the user picks **Other** and types an answer, the tag stores the typed text (up to 500 characters). * If the user clicks **Skip** or leaves the page, nothing is stored. The unsubscribe is already applied. To see the collected answers, click **View stats** on the **Survey** tile. You can also [segment users](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/) by the **`Unsubscribe Reason`** tag, for example, to analyze churn reasons. ## Other ways to unsubscribe Users can also opt out of all marketing emails in two ways: * Click **Unsubscribe from all** in the preference center. * Use the unsubscribe button of their email client. Both set the **Unsubscribed Emails** tag to `true`. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#full-opt-out-with-the-unsubscribed-emails-tag) --- # How to send emails **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/ Send emails via one-time campaigns or Customer Journey. Choose immediate delivery or automated multi-channel flows. --- Before you send emails, ensure you have [configured your project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) and verified your email and domain, and added subscribers by [using the API](https://docs.pushwoosh.com/developer/first-steps/subscribe-users#email) or [importing the mailing list](https://docs.pushwoosh.com/product/messaging-channels/emails/get-email-subscribers). Legal provisions Commercial email messages must comply with [the CAN-SPAM Act](https://www.ftc.gov/sites/default/files/documents/cases/2007/11/canspam.pdf). You need to comply if the primary purpose of your email is commercial. A primary purpose is commercial when your email contains primarily commercial content. Transactional or other non-commercial emails may not contain false or misleading information, but don’t fall under the CAN-SPAM Act regulations. [Learn more about how to comply with the CAN-SPAM Act](https://academized.com/blog/can-spam-act-compliance-guide-business) Pushwoosh requires the following in each email you send (normally in the footer): Company Address, City, State, Zip Code, Country. If you operate in the EU, make sure you comply with GDPR regulations. [Learn more](https://blog.pushwoosh.com/blog/gdpr-compliant-emails/) Pushwoosh offers two ways to send emails: #### One-time emails Send a single email to a segment, an email list, or all subscribers. Schedule it for now or a specific date and time. [Send one-time emails ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/) #### Email element in Customer Journey Add an email step to a journey and combine it with push, in-app, SMS, or WhatsApp. Trigger delivery by audience or events. [Send emails via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-emails-via-customer-journey/) Tip Use **one-time emails** for single campaigns and **Customer Journey** for automated, multi-channel flows. #### Submit transactional email over SMTP If a service already speaks SMTP, send transactional email through the SMTP gateway instead of calling the API directly. The gateway forwards each message to [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). [SMTP gateway ](https://docs.pushwoosh.com/developer/api-reference/smtp-gateway/) ## Delivery and compliance ### Bulk emails Please note that each recipient email address should be valid. Even if one of recipient email addresses is invalid, it might result in the whole delivery rejection and an increase in hard bounce rate. A recipient email address is the one specified in any destination field (To, Cc, Bcc). To help you avoid a significant increase in hard bounce rate and spam filtering, Pushwoosh does not send out bulk emails. When you target an audience segment (or your entire audience), we send a separate message to each recipient in this segment. Still, each email address you are sending to must be valid. Otherwise, your email deliverability can be affected, and [sending suspension](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/#sending-suspension) may come as a result. ### List-Unsubscribe header Pushwoosh adds a **List-Unsubscribe** header to each email so that mail clients (Gmail, Outlook, etc.) know where users can unsubscribe. Subscribers can opt out in one click from the mailbox interface. That keeps subscription status in order and reduces complaints and spam flags. ### Pre-send email audit Before you send a one-time email, run an audit in the one-time email wizard. Pushwoosh sends a test message to a verified address and checks content quality and compliance, including link health, unsubscribe headers, and spam indicators. [Learn how to audit your email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email). ### Sending suspension Pushwoosh always provides you with clean and trusted IP addresses, but in some cases, we can suspend your email delivery. Pushwoosh measures complaint and bounce rates across all projects in your account, over a limit period set by your plan (14 days by default). It checks them once at least 1,000 emails were sent in that period. When a rate crosses its limit, Pushwoosh pauses email sending in the project that received the bounce or complaint: * **Complaint rate:** if it reaches your plan’s limit (1% of emails sent by default), Pushwoosh pauses email sending in the project and suggests that you check email content and template. * **Hard bounce rate:** if it exceeds your plan’s limit (5% of emails sent by default), Pushwoosh pauses email sending in the project and checks your email database manually. * **Soft bounce rate:** if it exceeds 90% of emails sent, Pushwoosh pauses email sending in the project. * **Spam-related bounce rate:** if bounces whose server response mentions spam exceed 90% of emails sent, Pushwoosh pauses email sending in the project. Both hard and soft bounces count here, not only the **Soft Bounce Spam** type in [Email error types](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#email-error-types-you-may-see). To resume sending after a suspension, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). When bounces rise sharply, Pushwoosh sends a [bounce spike warning](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#bounce-spike-warning-before-email-sending-is-paused) before they reach these limits. --- # Send emails via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-emails-via-customer-journey/ Add an email step to a Customer Journey. Configure content, message type, subscription category, and launch the campaign. --- This guide explains how to send emails through Customer Journey. ## Create a campaign and set up the Entry ### Create a campaign Log in to your Pushwoosh account and navigate to **Customer Journey Builder.** Click **Create Campaign** to begin. You can either choose a journey from the templates or start from scratch. ### Add an entry element to the canvas If you’re starting from scratch, drag and drop an [entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) (Audience-based, Trigger-based, or API-based) as the first element of your journey. If you’re using a template, the entry element will already be on the canvas. Example: use **Audience-based Entry** to target loyal customers who have made purchases during past sales and are likely interested in the current promotion. ### Configure the Entry element Double-click on the Entry element to configure it. The settings depend on the entry type. For [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) you choose or create a segment. For [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) you select an event and conditions. For [API-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/) you use the API to add users. [Learn how to configure each entry type](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/). ![Entry element configuration dialog, options vary by entry type (Audience-based, Trigger-based, or API-based)](https://docs.pushwoosh.com/emails-send-email-15.webp) ## Add and configure the Email element Drag and drop the [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) onto the canvas and double-click to open its settings. In the pop-up window, start setting up your email. ### Give the journey point a name Example: *Black Friday Promo Email*. ![Email element name field for giving the journey point a descriptive name](https://docs.pushwoosh.com/emails-send-email-18.webp) ### Configure content In the **Content** step, configure the email body, subject, and sender details. #### Email preset 1. Select an existing preset from the dropdown, or click **Create content** to build new content. 2. To change the selected preset, click **Edit content** below the field. Tip **Create content** and **Edit content** open the email editor in a drawer over the journey canvas. You stay on the Email element while you edit. [Learn more](https://docs.pushwoosh.com/product/content/email-content/) ![Email preset field with Birthday offer selected and an arrow on the Newest first sort button](https://docs.pushwoosh.com/emails-send-email-19.webp) The preset list starts with the newest presets. To change the order, click the sort button above the dropdown. Save your content, then click the back arrow at the top of the editor to close the drawer and return to the Email element with the preset selected. #### Subject line Choose how to set the subject for this step: * **Custom:** Type a subject in the **Subject** field. This value is saved in the step and does not sync with preset changes. * **From preset:** Use the current subject from the selected preset. If the preset subject changes later, the step uses the updated value automatically at send time. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-the-subject-line) ![Subject line settings in Email element with Custom selected and a subject entered in the Subject field](https://docs.pushwoosh.com/emails-send-email-44.webp) #### Sender info Choose how to set sender details: * **From preset:** Use sender details from the selected preset. Updates in the preset apply automatically. If any part of the preset’s sender details is missing, the step fills it in from your [default sender details](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#add-default-sender-details). If those are missing too, the [pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) flags it before you can launch the journey. * **Specify manually:** Enter **From Name** and **From Email** in this step (must be a [verified domain](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/); the field suggests known mailbox names for each verified domain). Manual values are saved in the step and do not sync with preset changes. [How sender priority works](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#define-sender-details-and-reply-address) Optionally enable **Use this as the reply-to address to receive and track replies** so replies go to the From email. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#define-sender-details-and-reply-address) ![Sender info in Email element with Specify manually selected, From Name and From Email fields](https://docs.pushwoosh.com/emails-send-email-45.webp) ### Choose the message type * **Marketing message**: Respects subscription preferences, opt-outs, and frequency capping. Not sent to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: Sent to all users regardless of subscription status. Delivered to control group users. ![Email step configuration with Marketing message selected and Subscription category dropdown showing Product Updates](https://docs.pushwoosh.com/emails-send-email-28.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ### Select subscription category If you chose Marketing message, pick a category from the dropdown (e.g. Product Updates, Newsletter, Promotional) so the message aligns with your subscribers’ preferences. Categories are defined in the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). ### Overwrite personalization (optional) Configure how message content is personalized: * **Personalise message with event attributes**: Fill placeholders in the email with data from the journey (e.g. event or API-based entry). [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Overwrite Liquid placeholders**: Use values from a selected Event in Liquid tags. Tags that match event attribute names get those values. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Use Vouchers**: Insert a unique voucher code from a pool into the email (e.g. for promotions). [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#use-vouchers) ![Overwrite personalization section with Personalise message with event attributes, Overwrite Liquid placeholders, and Use Vouchers toggles in Email element](https://docs.pushwoosh.com/emails-send-email-29.webp) ### Delivery settings (optional) In this section you can set: * **Frequency capping**: Use global, ignore, or custom limit. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#configure-message-delivery-settings) * **Send rate**: Use global, without limit, or custom. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) * **Best time to send**: Deliver at the optimal time per user based on behavior. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#enable-best-time-to-send) ![Delivery settings with Frequency capping, Send rate, and Best time to send options](https://docs.pushwoosh.com/emails-send-email-30.webp) ### Split by email open (optional) Split the flow by whether users open the email or not. Set the wait period (up to 7 days). Example: follow up with a push or another email if the user doesn’t open. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#split-flow-depending-on-whether-this-message-is-opened-or-ignored) ![Split by email open status option with waiting period configuration settings](https://docs.pushwoosh.com/emails-send-email-22.webp) ### End the branch Once your email is configured, end the branch by adding an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. If you chose to split the flow, set up actions for users who ignore the email. ## Configure journey settings and launch ### Journey settings (optional) * Set [Silence period](https://docs.pushwoosh.com/product/customer-journey/journey-settings#silence-period) during which messages will not be sent to users * Set [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings#campaign-entry-limit) to define how many times a particular person can enter the journey within a specified period ### Set Conversion Goals Set Conversion Goals to track the success of your journey. Example: set up the CheckoutSuccess event as a Conversion Goal and monitor how many users achieve it within the journey. [Learn more about Conversion Goals in Journeys](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals) ### Launch the campaign Click **Launch campaign** to run the [Pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) and fix any listed issues, then confirm by clicking **Launch**. --- # Send one-time emails **URL:** https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/ Send a single email to a segment, an email list, or all subscribers. Select content, audience, schedule, and delivery settings in Control Panel. --- To send a one-time email in Pushwoosh, navigate to the **Campaigns** section, select **One-time messages**, and click **Send message** > **Email**. ![One-time messages menu showing Send message option with Email channel selection](https://docs.pushwoosh.com/emails-send-email-1.webp) ### Configure content In the **Content** step, select or create your email content. You can create content in multiple languages, either from scratch or using HTML code. #### Select existing content Use the **Type to search…** field or open the dropdown and select the desired email content. ![Email content search with three matching templates and an arrow on the Name A→Z sort button](https://docs.pushwoosh.com/emails-send-email-2.webp) To change the order of email content in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every email content dropdown, in one-time messages and journeys alike. Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, unapproved templates stay in the dropdown, but you can’t select them. ![One-time email form Content step showing an On approval template greyed out and unselectable in the dropdown](https://docs.pushwoosh.com/approvals-and-content-versions-9.webp) #### Create new content Note If content approval is on for your account, **Create new** isn’t available here. Submit and approve the template from **Content** → **Email content** first, then select it from the dropdown. 1. Click **Create new** and choose **Create from scratch…** ([Drag and Drop Editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/)) or **Create from HTML-code…** ([HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor/)). ![Create new dropdown with Create from scratch and Create from HTML-code options in the Content step](https://docs.pushwoosh.com/emails-send-email-37.webp) 2. Create the email in the editor. It opens in a drawer over the campaign form, so you stay on the Content step. 3. Save your content, then click the back arrow at the top of the editor. The drawer closes, and the new content is selected in the Content step. [Learn more about creating email content](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) #### Edit content Click **Edit content** above the preview to open the same drawer with the selected content loaded. ![Content step preview with Edit content button above the email preview](https://docs.pushwoosh.com/emails-send-email-38.webp) Save your changes, then click the back arrow at the top of the editor to close the drawer and return to the Content step. #### Select message type Choose how this email is treated for subscription and delivery rules: * **Marketing message**: Respects user subscription preferences, opt-outs, frequency capping, and the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts). Not sent to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: Sent to all users regardless of subscription status. Bypasses opt-outs, frequency capping, and the sunset policy. Delivered to control group users. ![Marketing message and Transactional message options in the Content step](https://docs.pushwoosh.com/emails-send-email-25.webp) Caution If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/#sending-is-blocked-until-content-is-approved) is on for your account, an unapproved template blocks the send even for a **Transactional message**. [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). #### Select subscription category If the subscription preference center is enabled for your project and you selected **Marketing message**, select a **Subscription category** from the dropdown so the message aligns with your subscribers’ preferences. Categories are defined in the [subscription preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). ![Subscription category dropdown with options such as Community, Promotional, Newsletter in email campaign form](https://docs.pushwoosh.com/emails-send-email-27.webp) ### Select the audience for your email campaign In the **Audience** step, define who will receive your email. Pushwoosh offers several targeting options to match your campaign goals: #### Send to segment Choose this option to target a predefined segment of your audience. This allows you to focus on specific groups based on their actions, behavior, or attributes. There are two options: * Select a segment from the drop-down list of pre-built options. ![Send to segment option with segment dropdown and Create segment button in audience settings](https://docs.pushwoosh.com/emails-send-email-4.webp) * To create a new segment, click **Create segment**. Then select one of the following actions from the dropdown menu: * **Build segment**: Create a new segment using the Segment builder. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) * **Import segment**: Import a segment from a CSV file. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment). ![Build or import segment options in email audience targeting step](https://docs.pushwoosh.com/emails-send-email-26.webp) #### Send to email list Use this option to send the email to a specific list of email addresses. Enter the email addresses as a list, separated by commas. This is ideal for one-time or special communications that don’t fit into a predefined segment. ![Send to email list option with text field for entering comma-separated email addresses](https://docs.pushwoosh.com/emails-send-email-5.webp) #### Send to all subscribers Select this option to target your entire subscriber base, regardless of their segment or behavior. Important When choosing this option, ensure the email content is relevant to a broad audience. Irrelevant messages may lead to increased unsubscribe rates. ![Send to all subscribers option selected in audience targeting settings](https://docs.pushwoosh.com/emails-send-email-6.webp) ### Ignore the sunset policy If your project has the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts) turned on, a marketing email skips contacts who haven’t opened or clicked your emails for the set period. To send this email to them too, turn on **Ignore sunset policy** below the audience. Use it for emails every contact must get, for example a change in your terms. Before you send, check what the switch does: 1. If you send to all subscribers, read the text under the switch. With the switch off, it gives the number of inactive contacts who won’t receive this email. With the switch on, it gives the number of inactive contacts who will receive it. 2. Open the **Review** step and check whether the sunset policy is applied or ignored. ![Ignore sunset policy switch turned off, with 34 inactive contacts won't receive this email and a Settings link below](https://docs.pushwoosh.com/emails-send-email-sunset-1.webp) Once you’ve set your audience, click **Next** to proceed to the scheduling step of your email campaign. ### Schedule email Then, choose when to send your email. You have several options: * Send it immediately * Schedule it for a specific date and time #### Send immediately Choose this option if you want to send the email as soon as the campaign setup is complete. This is ideal for urgent or time-sensitive messages that require immediate delivery. ![Send immediately option selected in email scheduling settings](https://docs.pushwoosh.com/emails-send-email-7.webp) #### Selected time Choose this option to schedule your email for a specific date and time. ##### Set the date and time Use the calendar to pick the exact date for your email. Then, specify the time in hours and minutes (using the 24-hour format). ##### Select the timezone * **Subscriber’s device timezone**. The email will be sent based on each recipient’s local timezone, ensuring delivery at the specified time in their location. * **Custom timezone**. Set a specific timezone to send the email at the same time for all recipients. This is useful for targeting users in a particular region. ![Selected time scheduling with date picker, time input, and timezone selection options](https://docs.pushwoosh.com/emails-send-email-8.webp) Tip You can cancel a scheduled email before it is sent. The message must have the **Scheduled** status. On the One-time messages list, open the actions menu (⋮) for the message and choose **Delete message**. #### Configure message delivery settings ##### Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive email messages, preventing over-messaging and reducing churn. Frequency capping isn’t available for a **Transactional message**. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 messages in 9 days, additional messages exceeding this limit will be skipped. ![Use global frequency capping settings option selected in message delivery configuration](https://docs.pushwoosh.com/push-notifications-one-time-push-25.webp) * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. ![Send messages without global frequency capping option selected in message delivery settings](https://docs.pushwoosh.com/push-notifications-one-time-push-26.webp) * **Use custom frequency capping** Set a custom message limit for this message in **Limit to a maximum of** N messages per N days. If the user exceeds this custom cap, the message is skipped for them. Under **Counting mode**, choose how sent messages are counted: * **Rolling window**: counts the messages sent within the last N×24 hours before each send, where N is the number of days in the limit. For example, with a limit of 3 messages per 2 days, a message sent Monday at 9 AM stops counting on Wednesday at 9 AM. * **Calendar day**: counts the messages sent today and on the previous N-1 calendar days, starting at midnight. With the same limit of 3 messages per 2 days, on Wednesday every message sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. Select a **Capping timezone** to set when midnight falls. If you don’t select one, calendar days are counted in UTC. ![Use custom frequency capping with message limit fields and Counting mode set to Calendar day with a Capping timezone selector](https://docs.pushwoosh.com/push-notifications-one-time-push-27.webp) Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other email messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). ##### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings option selected in send rate configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate option selected for maximum delivery speed](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Set the maximum number of messages sent per second, giving you full control over delivery speed. ![Use custom send rate option with input field for messages per second](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) Once you’ve configured the desired options, click **Next** to proceed to the audit step. ### Audit your email In the **Audit** step, Pushwoosh sends a real test email to an address you choose and checks it for issues that could hurt delivery, from broken links to missing unsubscribe mechanics. You must run the audit at least once before you can go to **Review**. [Learn more about email deliverability](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/). #### Run the audit 1. Click **Run audit**. ![Run audit button on the Audit step of the one-time email wizard](https://docs.pushwoosh.com/emails-send-email-35.webp) 2. Under **Send test to**, enter the email address the test should go to. It’s pre-filled with your own account email, but you can replace it with any address. An address on your account’s bounce list is rejected, with an error explaining why. 3. Click **Run audit** in the dialog to send the test email and run the checks. ![Run audit dialog with a Send test to email input field and a Run audit button in one-time email wizard](https://docs.pushwoosh.com/emails-send-email-36.webp) The results appear in two groups, **Content check** and **Compliance check**. Before the audit runs, both show **Not passed**. After the run, each group shows its result and the number of errors and warnings found. When to run the audit again * **After you change the content.** Click **Run again**. The results don’t update on their own: until you rerun the audit, they show the checks for the previous content. * **When you edit a scheduled email.** Each time you open the form, run the audit again before you can click **Apply changes**. #### What the audit checks ##### Content check * **Subject line:** Flags an empty subject line in any content language. * **Link health:** Flags broken links in the email body. * **Safe browsing:** Flags links marked as malware, phishing, a known shortener, a raw IP address, a suspicious top-level domain, or where the visible link text doesn’t match its destination. * **Click tracking:** Checks that links are wrapped by the click tracker. * **Email size:** Checks the email size against Gmail’s clipping limit. Oversized emails get clipped. Recipients see “[Message clipped]” and the unsubscribe link is hidden. * **Spam score:** Score from Pushwoosh’s antispam engine. It’s a useful indicator, not a guarantee of inbox placement at Gmail or Outlook. ##### Compliance check * **One-click unsubscribe headers:** Checks for List-Unsubscribe headers. If you send more than 5,000 emails a day, Gmail rejects messages without one-click unsubscribe or moves them to spam. * **Unsubscribe link health:** Checks that the unsubscribe link works. A broken link leads to spam complaints instead of opt-outs. * **Visible unsubscribe link:** Required by CAN-SPAM. Recipients must be able to opt out from within the email. * **Physical address:** Required by CAN-SPAM and most ESP policies. Your company’s street address must appear in the footer. If the audit finds issues The audit doesn’t block sending. If errors, warnings, or pending checks remain when you click **Next**, the **Continue bypassing the audit?** dialog opens. Click **Back to the audit** to fix the issues first, or **Confirm** to go to **Review** without fixing them. ![Continue bypassing the audit? dialog with an Errors found during email audit banner and Back to the audit and Confirm buttons](https://docs.pushwoosh.com/emails-send-email-47.webp) ### Review and edit your content In **Review**, check the preview and the summaries for content, audience, and schedule. Update anything that still needs to change before you send or schedule. #### Email details In **Email details**, review and edit the **Subject line**, **From name**, and **From email**. Enable **Use this as the reply-to address to receive and track replies** if you want replies to go to the same address. Clear it to enter a different **Reply-to name** and **Reply-to email**. The From and reply-to addresses must use a [domain verified](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) in your account. Otherwise, the message is rejected when you send it. ![Review step with Email details showing editable subject line, from name, from email, and email preview before scheduling](https://docs.pushwoosh.com/emails-send-email-9.webp) How sender details are determined Sender set in the one-time email form has the highest priority: it overrides sender in the [email content](https://docs.pushwoosh.com/product/content/email-content/) and [platform default](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/). The **From name** and **From email** values you set here are saved with this message and do not sync with changes in the content or platform. If you later change the sender in the content or platform settings, this message will still use the values set here. Both fields are required. They’re prefilled from the content or the platform default, so to use those values, keep them as they are. [How sender priority works in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#define-sender-details-and-reply-address) #### Resend to non-openers If you want to send the email again to users who did not open it, enable **Resend to non-openers** before you send or schedule the message. Pushwoosh then creates a follow-up send to those recipients using the same email content. 1. Enable **Resend to non-openers**. 2. Set **Send date**, **Time**, and **Timezone** for when the follow-up should be delivered. 3. Edit the follow-up **Subject line** if needed. It’s prefilled from the content, can differ from the original subject, and can’t be empty. ![Review step with Resend to non-openers enabled, showing send date, time, timezone, and follow-up subject line](https://docs.pushwoosh.com/emails-send-email-31.webp) Tip The earliest resend date is 3 days after the original send date. A resend 3–5 days later usually improves engagement and open rates because people have more time to see and open the first email. If the follow-up lands too soon, you often get fewer opens and weaker engagement. #### Link to Aggregated Messages Optionally, link your email to an [Aggregated messages campaign](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/): enable **Aggregated statistics** and select the campaign in **Link message statistics with**. This allows you to group the email with related messages and track their combined performance over time in the Aggregated Messages section. Use this feature to monitor message effectiveness as part of a broader communication strategy. #### Name and confirm your send 1. Click **Schedule message** or **Send now**. The **Confirm sending** dialog opens. 2. Optionally edit **Campaign name**. It’s prefilled with the email’s subject line. Keep it to 255 characters or fewer: longer text is cut off without a warning. 3. Click **Schedule message** or **Send now** in the dialog. ![Confirm sending dialog with Campaign name field prefilled from the email's subject line](https://docs.pushwoosh.com/emails-send-email-46.webp) The campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the subject line. If you clear the field, Message History shows the subject line. A campaign name only labels this one send. To group the email with other messages, link it to an Aggregated Messages campaign (see above). #### Cancel or edit a scheduled email For emails that are scheduled and not yet sent (status **Scheduled**), you can change or cancel them. Open the list of messages in **Campaigns** → **One-time messages**. Then, open the actions menu (⋮) for the message you want to change or cancel: * **Delete message:** cancels the scheduled delivery and changes the status to **Stopped**. * **Edit message:** opens the one-time email form with the message data loaded. * **Rename:** sets or changes the campaign name without opening the edit form. The limit is 255 characters. To remove the name, clear the field and save. The row shows the email’s subject line again. While editing * You can update **content** and **audience**. * In **Review**, you can change the **subject line** without reopening the email builder. * In **Review**, you can change **Resend to non-openers**, including the follow-up **Subject line**, **Send date**, **Time**, and **Timezone**. * The scheduled send time cannot be changed. To reschedule, use **Delete message** and create a new message. * On the **Audience** step you can change the segment, create a new one, or change the email list. The audience size is recalculated after you make a change. * If the message starts sending while you are editing, saving your changes fails with an error, and the message stays unchanged. * In Review, the button is labeled **Apply changes** instead of **Schedule message** or **Send now**. Click it, then confirm again in the **Confirm sending** dialog to save your changes, including any edit to the campaign name. ### Example scenario Imagine you are the marketing manager of an e-commerce platform, and you want to send an email promoting an upcoming New Year Sale to a targeted group of users who have previously made purchases. The email should be delivered at a specific date and time, right before the sale begins. You can set up this campaign in Pushwoosh as follows: **1.** First, navigate to **Campaigns**, and select **One-time messages**. Click **Send message**, then choose **Email** to start setting up your one-time email campaign. **2.** You decide to use existing email content for this campaign. In the **Content** step, open the dropdown menu and select the pre-built email content titled **New Year Sale Promo**. Review the email content: “Our New Year Sale is just around the corner! 🎉 Get ready to enjoy up to 50% off on your favorite products. The sale kicks off on January 1st, so mark your calendars and make sure you don’t miss out! 🛍️✨” ![Email content selection showing New Year Sale Promo template in dropdown menu](https://docs.pushwoosh.com/emails-send-email-10.webp) Once you’re satisfied with the content, click **Next** to move to the audience selection step. **3.** Since you want to target users who have made previous purchases, choose **Send to segment**. From the dropdown menu, select the segment **Returning customers**, which is based on users who made purchases within the past year. ![Audience selection with Send to segment option and Returning customers segment selected](https://docs.pushwoosh.com/emails-send-email-11.webp) After confirming the audience, click **Next** to proceed to the scheduling step. **4.** To ensure the email is sent at the right time, select **Selected time** in the scheduling options. * Set the date to December 30 and the time to 10:00 AM using the 24-hour format. * Choose **Subscriber’s device timezone** so the email is sent at 10:00 AM in each recipient’s local timezone, regardless of their location. **5.** In the **Audit** step, click **Run audit**, enter a test email address, and run the checks to confirm the email has no broken links or missing unsubscribe mechanics before it goes out. **6.** Review all the campaign details, including the audience, schedule, and email content. Use the Preview feature to see how the email will appear in recipients’ inboxes. ![Campaign review screen showing scheduled time, audience segment, and email content preview](https://docs.pushwoosh.com/emails-send-email-12.webp) Once everything is verified, click **Schedule message**. In the **Confirm sending** dialog, enter **New_Year_Sale_Email** in **Campaign name** so the send is easy to find in Message History, and click **Schedule message**. --- # Getting started with Facebook Messenger messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/ Send Facebook Messenger messages via Customer Journeys or one-time messages. Includes setup steps, benefits, and prerequisites. --- Facebook Messenger lets you reach customers on one of the most widely used messaging platforms. With Pushwoosh, you can send Facebook Messenger messages as part of your marketing campaigns through Customer Journeys or one-time messages. ## What you can do with Facebook Messenger messages With Facebook Messenger in Pushwoosh, you can: * Send promotional offers and announcements while the 24-hour messaging window is open (or with a valid message tag when Meta allows that use case). * Notify users about order confirmations, delivery updates, or appointment reminders (often with a message tag such as **POST_PURCHASE_UPDATE** or **CONFIRMED_EVENT_UPDATE** outside the window). * Trigger messages based on user behavior or external events. * Use Facebook Messenger alongside other channels, such as push notifications, email, and SMS, to deliver unified, [omnichannel user experiences](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/). Pushwoosh automatically logs `PW_FBMessengerReceived` and `PW_FBMessengerStarted` events whenever a user messages your Page. In Customer Journey Builder, use these as **Trigger-based Entry** events. [Learn more about entry triggers](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#entry-triggers) ## Prerequisites for sending Facebook Messenger messages Developer assistance needed You’ll need assistance from your developer team for the Meta App, tokens, webhook, and App Review. You connect the Page in Pushwoosh after they provide the Page ID and Page Access Token. Share the configuration guide with them to get started. To start sending Facebook Messenger messages with Pushwoosh, make sure the following requirements are met: 1. **Set up your Facebook Page and Meta App** Create a Meta App with the **Messenger** product and connect it to your Facebook Page. 2. **Connect Facebook Messenger to Pushwoosh** Enter your Page ID and Page Access Token in Pushwoosh, then set the webhook URL in your Meta App dashboard. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/) 3. **Get Meta App Review approval** Sending only works once Meta approves your Meta App’s Messenger permissions for the connected Page. ## Send Facebook Messenger messages You can send Facebook Messenger messages in Pushwoosh using the following methods: ### One-time Facebook Messenger messages Reach your Messenger audience with a single campaign, sent immediately or on a schedule. [Send one-time Facebook Messenger messages ](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/) ### Facebook Messenger in Customer Journey Automate Messenger replies after someone interacts with your Page, and combine them with push, email, and other channels in one flow. [Send Facebook Messenger messages via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-via-customer-journey/) Tip Use **one-time messages** for a single campaign and **Customer Journey** for automated flows after inbound Page interactions. ### Automate messaging via API Your development team can send Facebook Messenger messages programmatically with Messaging API v2. Developer assistance needed Setting up Facebook Messenger messaging via API requires help from your developer team. Please share the link below with them for proper implementation. [Learn more about sending Facebook Messenger messages via API ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#facebook-messenger-fb_messenger/) ## Limitations * **Buttons, media, and cards only in Customer Journey.** One-time messages and **Message Text** in a journey are plain text. For buttons, media, or cards, select a Messenger preset built from a [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) in the journey element. * **24-hour free reply window.** After a user messages your Page, you can send free-form content for 24 hours. * **A message tag is required outside that window.** Outside the 24-hour window, pick a [message tag](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#use-a-message-tag-outside-the-24-hour-window) that matches Meta’s approved use cases, or Meta rejects the send. Pushwoosh does not independently track window state and surfaces Meta’s error when the window is closed and no tag is set. * **No per-message read status.** You can’t confirm that one specific outbound Messenger message was read. * **Live sending depends on Meta App Review.** The channel only delivers once Meta has approved your Meta App’s Messenger permissions for the connected Page. --- # Send Facebook Messenger messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/ Send a one-time Facebook Messenger message from Pushwoosh, pick a message tag, set your audience, and schedule delivery. --- Before you start Connect your Facebook Page in **Settings → Configure platforms → Facebook Messenger** before you create a campaign. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/) To send a one-time Facebook Messenger message in Pushwoosh: 1. Go to **Campaigns**. 2. Open **One-time messages**. 3. Click **Send message** → **Facebook Messenger**. ![One-time messages menu with Send message and Facebook Messenger channel selected](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-7.webp) ## Compose your message In the **Content** step, set the message type, enter the text, and choose a message tag if you need one. ### Select message type Choose how this message is treated for subscription and delivery rules: * **Marketing message**: Respects frequency capping. Not sent to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: Sent to all users regardless of subscription status. Bypasses frequency capping. Delivered to control group users. Message type does not replace Meta’s 24-hour window or message-tag rules. [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ![Marketing message and Transactional message options on the Facebook Messenger content step](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-1.webp) ### Enter message text Enter your message in the **Message text** field. Note One-time Facebook Messenger messages are plain text. To send buttons, media, or cards, use a Messenger preset in [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#select-message-content). ![Message text field on the Facebook Messenger content step with character count](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-5.webp) ### Use a message tag outside the 24-hour window Meta only delivers free-form content inside the 24-hour window opened by an inbound message from the user. Leave **Message tag** set to **No tag** when you are sending inside that window. Outside that window, select a **Message tag** matching one of Meta’s approved use cases, or the send fails: * **CONFIRMED_EVENT_UPDATE**: a reminder or update about an event the recipient is scheduled to attend. * **POST_PURCHASE_UPDATE**: an update related to a purchase the recipient already made. * **ACCOUNT_UPDATE**: a non-recurring update about a change to the recipient’s account. * **HUMAN_AGENT**: a message from a human agent responding to the recipient. Choose a tag that matches the real reason for the message. [Learn more about message tags and closed windows](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#use-a-message-tag-outside-the-24-hour-window). ![Message tag dropdown on the Facebook Messenger content step set to No tag](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-6.webp) ## Select the audience for your Facebook Messenger campaign In the **Audience** step, define who will receive your message. ![Facebook Messenger audience step with Send to segment and Send to all subscribers options](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-2.webp) ### Send to segment Target a predefined [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of your audience based on tags, events, or behavior. Select a segment from the dropdown, or click **Create segment** to [build](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) or [import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment) one. ### Send to all subscribers Target your entire Facebook Messenger-reachable audience. Once you’ve set your audience, click **Next** to proceed to the **Sending time** step. ## Schedule Facebook Messenger message In the **Sending time** step, choose when to send your Facebook Messenger message. ### Send immediately Send the message as soon as the campaign setup is complete. This is ideal for urgent or time-sensitive messages. ### Selected time Schedule your message for a specific date and time. * **Set the date and time**: use the calendar to pick the date, then specify the time in hours and minutes (24-hour format). * **Select the timezone**: * **Subscriber’s device timezone**: the message is sent at the specified local time for each recipient. * **Custom timezone**: the message is sent at the same time for all recipients, useful for targeting a particular region. ![Facebook Messenger scheduling with Send immediately and Selected time options, date picker, and timezone selection](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-3.webp) ### Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive Facebook Messenger messages, preventing over-messaging. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is 3 messages in 9 days, additional messages exceeding this limit are skipped. ![Use global frequency capping settings option selected in message delivery configuration](https://docs.pushwoosh.com/push-notifications-one-time-push-25.webp) * **Send messages without global frequency capping** The user receives this message even if they’ve exceeded the channel’s message limits. Use with caution to avoid over-messaging. ![Send messages without global frequency capping option selected in message delivery settings](https://docs.pushwoosh.com/push-notifications-one-time-push-26.webp) * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message is skipped. Under **Counting mode**, choose how sent messages are counted: * **Rolling window**: counts the messages sent within the last N×24 hours before each send, where N is the number of days in the limit. For example, with a limit of 3 messages per 2 days, a message sent Monday at 9 AM stops counting on Wednesday at 9 AM. * **Calendar day**: counts the messages sent today and on the previous N-1 calendar days, starting at midnight. With the same limit of 3 messages per 2 days, on Wednesday every message sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. Select a **Capping timezone** to set when midnight falls. If you don’t select one, calendar days are counted in UTC. ![Use custom frequency capping with message limit fields and Counting mode set to Calendar day with a Capping timezone selector](https://docs.pushwoosh.com/push-notifications-one-time-push-27.webp) ### Set send rate limits The **Send rate** setting controls how quickly messages go out. Use it when you want a steadier send instead of delivering the whole audience as fast as possible. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages are sent immediately. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings option selected in send rate configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid delivery spikes. ![Send messages without global send rate option selected for maximum delivery speed](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Specify the number of messages sent per minute for full control over delivery speed. ![Use custom send rate option with input field for messages per minute configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) ## Review and send In **Review**, check the preview and the summaries for content, audience, and schedule. Update anything that still needs to change before you send. If you schedule a send without a message tag, Meta delivers it only to users who wrote to the Page within the 24 hours before it fires. Pushwoosh still sends it to the whole audience, and every send outside the window fails with **Outside Allowed Window** (403004) on the **Errors** tab of the [message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#errors-tab). To reach people outside the window, [use a message tag](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/#use-a-message-tag-outside-the-24-hour-window). ![Facebook Messenger campaign review step showing message preview, audience, and schedule summary before sending](https://docs.pushwoosh.com/messaging-channels-facebook-messenger-one-time-4.webp) Click **Schedule send** or **Send now** to open a **Send this message?** dialog with a **Campaign name** field: a free-text label for this one message. Campaign name is prefilled with the message text you entered in **Message text** (a multi-line message prefills as a single line). Clear it and Message History falls back to its previous default, the same as before this field existed. Keep it to 255 characters or fewer. Typing past the limit truncates the text without a warning. ![Send this message confirmation dialog with Campaign name field prefilled from the Facebook Messenger message text](https://docs.pushwoosh.com/fb-messenger-one-time-confirm-sending.webp) Once set, the campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export. Confirm again in the dialog to finish, depending on your selected scheduling options. ## Manage a scheduled message For Facebook Messenger messages that are scheduled and not yet sent (status **PENDING**), you can change or cancel them. Open the list of messages in **Campaigns → One-time messages**, then open the actions menu (⋮) for the message: * **Edit message**: opens the Facebook Messenger form with the message data loaded. You can update the content, message tag, audience, schedule, and delivery settings. In Review, the button is labeled **Apply changes** instead of **Schedule send** or **Send now**. Click it, then confirm again in the **Send this message?** dialog to save your changes, including any edit to the campaign name. * **Rename**: opens a small dialog to set or change the campaign name, without reopening the full edit form. Same 255-character limit as the Campaign name field you set at send time. Clear the field and save to remove the name. The row reverts to its previous default title. * **Delete message**: cancels the scheduled delivery and changes the status to **Canceled**. Other ways to send Facebook Messenger messages You can also send Facebook Messenger messages using [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with the [`fb_messenger` payload block](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#facebook-messenger-fb_messenger). This requires assistance from your developer team. One-time messages here cover single broadcast sends only. For automated flows triggered by inbound messages or other events, use Facebook Messenger in [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-via-customer-journey/) instead. --- # Send Facebook Messenger messages via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-via-customer-journey/ Send personalized Facebook Messenger messages with Pushwoosh Customer Journey, triggered when a user messages your Page. --- Send Facebook Messenger messages through Pushwoosh Customer Journey, triggered automatically when a user interacts with your connected Facebook Page. Tip For a single broadcast sent right away or scheduled once, use [one-time Facebook Messenger messages](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/) instead. ## Prerequisites Connect your Facebook Page in [Facebook Messenger platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/) before you build a journey. Otherwise, sending Facebook Messenger messages will not be available. Live sending also needs Meta App Review approval for your Meta App’s Messenger permissions. ## Create a Facebook Messenger journey 1. Go to **Campaigns → Customer Journey Builder**, then click **Build a journey from scratch**. 2. Add a **Trigger-based Entry**. Choose: * **`PW_FBMessengerReceived`** if the journey should start when the user sends a message, or * **`PW_FBMessengerStarted`** if it should start when they tap a menu item, quick reply, or open a referral link (this can happen more than once per user). [Learn more about entry triggers](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#entry-triggers) 3. Add a [**Facebook Messenger** element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/) and connect it to the entry trigger. ![Facebook Messenger element connected to a Trigger-based Entry element on the Customer Journey canvas](https://docs.pushwoosh.com/journey-elements-facebook-messenger-3.webp) 4. Click the **Facebook Messenger** element to open its settings panel. 5. Select a Messenger **Preset**, or enter your own **Message Text**. Presets are copies of [rich messages](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/), so they can carry buttons, media, and cards. Create them in **Content > Messenger presets**. ![Facebook Messenger element with message text, message tag, and Use Vouchers toggle](https://docs.pushwoosh.com/journey-elements-facebook-messenger-1.webp) 6. Set a **Message tag** if you need to send outside the 24-hour window. Leave **Message tag** set to **No tag** when you are sending inside that window. Outside the window, Meta requires a tag matching one of its approved use cases: * **CONFIRMED_EVENT_UPDATE**: a reminder or update about an event the recipient is scheduled to attend. * **POST_PURCHASE_UPDATE**: an update related to a purchase the recipient already made. * **ACCOUNT_UPDATE**: a non-recurring update about a change to the recipient’s account. * **HUMAN_AGENT**: a message from a human agent responding to the recipient. Choose a tag that matches the real reason for the message. [Learn more about message tags](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#use-a-message-tag-outside-the-24-hour-window) 7. (Optional) To attach a voucher code, include `{{voucher}}` in your message text, then: 1. Toggle **Use Vouchers** to ON. 2. In **Voucher Pool**, select the pool with your codes. Add the pool in advance. [Learn more about vouchers](https://docs.pushwoosh.com/product/content/vouchers/) 3. (Optional) In **Assign Tag**, apply a tag to users who receive a voucher. ![Facebook Messenger element Voucher Pool and Assign Tag fields shown after enabling Use Vouchers](https://docs.pushwoosh.com/journey-elements-facebook-messenger-2.webp) 8. Click **Apply** to save the element settings. 9. (Optional) Complete the journey with an [Exit element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/). A step with no next step also ends that path. 10. When you’re ready, click **Launch journey** to activate it. ## Limitations * **Message Text** is plain text only. For buttons, media, or cards, select a Messenger preset. * Outside the 24-hour window opened by an inbound message, you need a [message tag](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#use-a-message-tag-outside-the-24-hour-window), or Meta rejects the send. * You can’t confirm that one specific Messenger message in the journey was read. * The channel only delivers once Meta has approved your Meta App’s Messenger permissions for the connected Page. --- # Feed campaigns **URL:** https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/ Send every new item from an RSS or Atom feed to your subscribers automatically, as a push notification, an inbox message, or both. --- A feed campaign watches one of your [news feeds](https://docs.pushwoosh.com/product/content/news-feeds/) and sends each new item to your subscribers the next time Pushwoosh checks the feed. You set the campaign up once: which feed and category to follow, which channel to send through, and who gets it. From then on, every new item goes out on its own, with no manual sends. Each message is built from the item itself: its title, description, image, and link. ## Prerequisites * A feed registered under **Content → News Feeds**, or the feed’s URL so you can register it while creating the campaign. * For **Inbox** and **Push and Inbox**, [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) set up in your app or on your website. Share the [mobile Message Inbox guide](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox) or the [web Message Inbox guide](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) with your developers. ## Create a feed campaign Go to **Campaigns → Feed campaigns** and click **Create campaign**. The wizard has four steps: **Content**, **Audience**, **Settings**, and **Review**. Fill them in one by one, clicking **Next** after each. ### Choose the feed and channel On the **Content** step: 1. In **Feed**, pick a registered feed. Its URL appears under the field. To register a new one without leaving the campaign, click **Create new** and [add the feed](https://docs.pushwoosh.com/product/content/news-feeds/#register-a-feed). 2. In **Category**, pick a category to send only that category’s items, or keep **All categories** to send every item. The list is built from the categories of the feed’s recent items. ![Feed field with the Daily Brief feed and its URL, and Category set to All categories](https://docs.pushwoosh.com/feed-campaigns-2.webp) 3. Under **How should we send new items?**, pick a channel: * **Push:** a notification on the device. * **Inbox:** a message in the app’s inbox. * **Push and Inbox:** a notification that’s also saved to the inbox. ![Channel cards Push, Inbox, and Push and Inbox with Push and Inbox selected under How should we send new items](https://docs.pushwoosh.com/feed-campaigns-4.webp) 4. For **Inbox** or **Push and Inbox**, set **Remove from inbox after** to how many days a message stays in the inbox, from 1 to 365. It defaults to 1 day. ![Remove from inbox after field at its default of 1 day, with the up and down arrows](https://docs.pushwoosh.com/feed-campaigns-5.webp) Check the preview on the right to see how the feed’s newest item looks as a push and as an inbox message. If an item has no description, its title becomes the message text and the message goes out without a separate title. ### Choose who gets the messages On the **Audience** step, pick who gets the messages: * **Send to segment:** pick a segment from the list, or click **Create segment** to build or import a new one. * **Send to all subscribers:** every subscriber of the application. ![Audience step with the Send to segment option selected, a segment picked from the list, and the Create segment button](https://docs.pushwoosh.com/feed-campaigns-6.webp) A segment that a feed campaign uses can’t be deleted. To delete it, pick another audience for the campaign or delete the campaign first. ### Set sending options On the **Settings** step: 1. In **Feed updates**, check how often Pushwoosh looks for new items. The interval belongs to the feed. To change it, click **Edit** next to it and save the feed. The new interval applies to every campaign that uses this feed. 2. In **If several items are published at once**, choose what to do when one check finds more than one new item: * **Send only the newest:** the default. One item goes out per check. * **Send up to 2, one per minute**, **Send up to 3, one per minute**, or **Send up to 5, one per minute:** the newest items go out first, a minute apart. Items over the limit wait for the next check. They go out then if they were published within the last two check intervals. ![Settings block with Feed updates every 15 minutes and If several items are published at once set to Send up to 3](https://docs.pushwoosh.com/feed-campaigns-3.webp) 3. For **Push** or **Push and Inbox**, set **Time to live**: how long a push is kept for delivery while the device is offline. It defaults to 1 week. 4. For **Push** or **Push and Inbox**, set **Send rate**, or keep **Use global send rate settings**. See [Send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/). ![Sending options with the frequency capping banner, Time to live set to 1 week, and Send rate on global settings](https://docs.pushwoosh.com/feed-campaigns-7.webp) Caution Feed campaigns bypass [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). Every new item is delivered, even to users who have already reached their frequency cap. With a busy feed, narrow the campaign with **Category** and keep **Send only the newest**. ### Review and launch On the **Review** step: 1. Check the summary of **Content**, **Audience**, and **Settings**. To fix a section, click **Change options** next to it. ![Review step with the summary of Content, Audience, and Settings, each with Change options](https://docs.pushwoosh.com/feed-campaigns-8.webp) 2. Click **Launch campaign** at the top right. 3. Enter **Campaign name** and confirm. The campaign starts active. Only items that Pushwoosh picks up from the feed after you launch it are sent. Items already in the feed are not. ## Track your campaigns The **Feed campaigns** page lists your campaigns. Use **Search** to find a campaign, and the **All**, **Active**, and **Inactive** tabs to filter by status. ![Feed campaigns list with three campaigns showing status, feed updates, channel and audience, and last sent item](https://docs.pushwoosh.com/feed-campaigns-1.webp) For each campaign, the table shows: * **Name**, with the feed’s URL under it. * **Status:** **Active** or **Inactive**. * **Feed updates:** how often the feed is checked, for example **Every 15 min** or **Every 1 hour**. * **Sending:** the channel (**Push**, **Inbox**, or **Push, Inbox**), and the audience under it, either **All subscribers** or **Segment:** followed by the segment’s name. * **Last sent:** the title of the last item sent and when it went out, or **Nothing sent yet**. To see delivery statistics for the messages a campaign sent, click its name. ## Manage a campaign Click a row’s menu (**⋮**) to manage a campaign: ![Row menu of an active campaign with View statistics, Deactivate…, Edit, Rename…, and Delete…](https://docs.pushwoosh.com/feed-campaigns-9.webp) * Click **View statistics** to open the campaign’s message statistics. * Click **Edit** to change any step of the campaign, then click **Save changes**. The new settings apply from the next feed check. * Click **Rename…** to change the campaign’s name. * Click **Deactivate…** to stop sending. * On an inactive campaign, click **Activate…** to resume sending new items. Items published during a long pause won’t be sent later. If you activate the campaign again within two check intervals, recent items it missed can still go out. * Click **Delete…** to stop sending and remove the campaign. This can’t be undone, so deactivate the campaign instead if you only want to pause it. If you delete or deactivate the feed * A feed that a feed campaign uses can’t be deleted from **News Feeds**. Delete the campaign first. * If you deactivate the feed in **News Feeds**, its campaigns send nothing until you activate the feed again. If Pushwoosh can’t read the feed, its campaigns send nothing until the feed is read again. **News Feeds** shows **Error: Couldn’t read this feed** under the feed’s name. --- # Getting started with in-apps **URL:** https://docs.pushwoosh.com/product/messaging-channels/in-apps/ Learn how to create, personalize, and track in-app messages to drive user engagement and boost conversions with Pushwoosh. --- [Youtube Video: Use deeply contextual in-apps to capitalize on user engagement](https://youtube.com/watch?v=QWM1bXf1kZY) In-app messages appear directly within your app’s interface while a user is actively using it. This makes them a less disruptive way to communicate compared to push notifications, emails, or SMS messages. These messages always feature Rich Media elements like images and videos, along with formatted text, interactive links, and CTA buttons. This allows you to deliver visually engaging, action-driven messages that enhance user experience and boost engagement. ![Example of an in-app page in a mobile app](https://docs.pushwoosh.com/rich-media-README-1.webp) ## What can you do with in-apps? * **Onboard new users**: guide users through app features with welcome messages and tutorials. [Learn more](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/#2-welcome-in-app) * **Deliver personalized promotions**: show targeted offers and discounts based on user behavior. [Learn more](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message/) * **Send timely reminders**: nudge users about abandoned carts, subscription renewals, or expiring deals. * **Announce new features and updates**: keep users informed about app improvements and enhancements. * **Automate messaging**: use **Pushwoosh Customer Journey** to trigger in-app messages based on user behavior and engagement. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) ## Prerequisites for sending in-app messages Developer assistance needed You’ll need assistance from your developer team for these steps. Share the links below with them to get started. Before you can send in-app messages, you must complete the following steps: 1. **Configure your project platform** Ensure your project platform is configured in Pushwoosh. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) 2. **Integrate the Pushwoosh SDK** Add the Pushwoosh SDK to your app for in-app messaging functionality. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Start subscribing users** [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) ## Create and customize in-app messages * **In-app builder**: Design in-app messages using the built-in editor. [Learn how to build in-apps ](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) * **Upload existing templates**: Incorporate pre-designed templates by uploading them as ZIP files. [Learn how to upload existing templates ](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/) * **Use pre-built in-app templates**: Streamline your workflow with ready-to-use templates. ### Choose the right in-app message format Pushwoosh supports different in-app message formats to match your campaign goals: * **Full-screen in-apps**: best for onboarding, paywalls, and promotions. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) * **Part-screen in-apps**: compact overlay messages shown as center cards or at the top or bottom of the screen. Often used for opt-in prompts, announcements, and promo content. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) * **Modal Rich Media in-apps**: A part-screen message that appears in the center, top, or bottom of the screen without covering the entire app. It can disappear automatically or be closed with a swipe—ideal for subtle offers, updates, or key actions. * **Native in-apps**: SDK-rendered formats drawn natively instead of in a WebView, for smoother animations. Modal, fullscreen, stories (iOS SDK 7.2.0+ / Android SDK 6.10.0+), sheet, carousel, and banner (iOS SDK 7.2.1+ / Android SDK 6.10.1+) work on both platforms; video, pip, scratch card, and spin wheel are iOS-only for now. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/#native-sdk-rendered-templates) Developer assistance needed To use this format, developer setup is required. Please share these guides with your development team: * [iOS Modal Rich Media](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-modal-rich-media/) * [Android Modal Rich Media](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-modal-rich-media/) [Learn more about in-app message formats ](https://www.pushwoosh.com/blog/what-are-in-apps/#in-app-formats) ### Personalize in-app messages for better engagement Personalization enhances user experience by dynamically inserting user-specific details. [Learn more about how to personalize in-apps ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message/) ## Send in-app messages to your audience Once your in-app template is ready, you can send in-app messages to your users. [Learn more about sending in-app messages ](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/) ## Enhance in-app messages with JavaScript Pushwoosh offers **JavaScript-enabled in-apps**, allowing advanced dynamic and interactive content. * Track user interactions in real time. * Update user tags dynamically. * Create highly personalized experiences. Developer assistance needed JavaScript-enabled in-app messages require developer setup. Please share [this link](https://docs.pushwoosh.com/developer/guides/messaging-channels/inapp-with-javascript) with them for proper implementation. ## Track and analyze in-app message performance Pushwoosh provides detailed analytics to help you measure in-app engagement and optimize your messaging strategy. * **Per in-app statistics** Track impressions, user interactions, and audience insights. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) * **Campaign-level insights** Analyze in-app performance in Customer Journeys alongside push notifications, emails, and SMS interactions. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#communications/) * **Monitor custom rich media performance** Track button clicks, form submissions, and other interactive elements in your custom in-app messages. [Learn more ](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax#monitor-performance-of-custom-rich-media/) --- # How to send in-app messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/ Send in-app messages via one-time campaigns or Customer Journey. Choose instant delivery or automated journeys. --- Pushwoosh offers two ways to send in-app messages, depending on when and how you want them to appear: #### Instant (one-time) in-apps Display a message immediately when a specific event occurs inside the app. Best for real-time engagement. [Send instant in-apps ](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) #### In-app element in Customer Journey Schedule an in-app message to be delivered the next time the user opens the app. Combine in-app messages with push, email, SMS, and WhatsApp for a multi-channel flow. [Send in-apps via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/) Tip Choose **instant in-apps** for real-time engagement and **Customer Journey** for automated messaging that reaches users when they return to the app. --- # Send in-apps via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/ Deliver in-app messages through Customer Journey. Add an In-app element, set entry and scheduling, then launch the campaign. --- Tip Prepare an [in-app template](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) first, then create a journey to deliver it to your users. This guide walks you through sending an in-app message via Customer Journey. ## Create a journey and add elements 1. Go to **Customer Journey Builder → Create Campaign**. 2. Drag **Entry**, **In-App**, and **Exit** onto the canvas and connect them in that order. **Exit** is optional. You can leave the branch without an outgoing connection instead. [](https://docs.pushwoosh.com/in-apps-sending-in-apps-2.webm "Animated demonstration of dragging Entry, In-App, and Exit elements onto the journey canvas") ## Configure entry and audience You must configure the **Entry** element; otherwise, the in-app message will not be delivered. ### Trigger-based entry Show the in-app when a user performs a specific action (for example, lands on a pricing screen). Use default triggers and events or [create your own](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). [Learn more about trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). [](https://docs.pushwoosh.com/in-apps-sending-in-apps-3.webm "Animated demonstration of configuring trigger-based entry with iOS platform filter") Example: show an in-app with a discount only to iOS users by adding a platform filter. ### Audience-based entry Start the journey for a segment of users (for example, your entire user base). Create a segment from tags and/or events or use [pre-built segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments). [Learn more about audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). [](https://docs.pushwoosh.com/in-apps-sending-in-apps-4.webm "Animated demonstration of configuring audience-based entry with segment selection") #### Configure scheduling In the [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) element, **Entry schedule** controls when users enter. By default, **One-time entry** with **Immediately** makes users enter as soon as the journey is activated. To run on a specific date and time (with timezone), choose **One-time entry** → **Schedule for a specific day**. To run on several dates or at a fixed interval, choose **Recurring entry**. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#entry-schedule) ![Entry schedule with One-time entry and Recurring entry options](https://docs.pushwoosh.com/journey-elements-audience-based-entry-2.webp) ## Configure the in-app element 1. Click the **In-App** element → **Media Source**. Select a template. The list starts with the newest templates. To change the order, click its sort button. 2. Set **In-app expiration**, the number of days the message is available, from 1 to 7. For example, set 7 days if the offer expires in a week. ![In-App element with Welcome Newcomers in Media Source, an arrow on the Sort: Newest first button, In-app expiration of 7 days, and Show in-app options](https://docs.pushwoosh.com/in-apps-sending-in-apps-14.webp) 3. In **Show in-app**, choose **On next app launch** or **On event**. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/#choose-when-to-show-the-in-app). 4. Choose the message type: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: frequency capping and the silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![In-app element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/in-apps-sending-in-apps-15.webp) [Learn more about the difference between marketing and transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). 5. Configure frequency capping. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/#set-frequency-capping). 6. Click **Save** to save the settings. ## Validate and launch the journey 1. Finish configuring the journey and run the [pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) to validate the journey. 2. Click **Launch campaign** (top-right) to send the in-app message to users. ![Screenshot showing the completed journey canvas with Launch campaign button highlighted](https://docs.pushwoosh.com/in-apps-sending-in-apps-6.webp) --- # Send instant in-apps **URL:** https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/ Send one-time in-app messages triggered by events. Define creative, audience, event trigger, and capping in the Control Panel. --- Tip For automated flows that combine in-app with push, email, and other channels, use [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/) instead. Instant in-apps deliver messages to users the moment a specified action happens inside the app, maximizing the chance they will be seen. To send in-app messages through in-app campaigns: 1. Navigate to **Campaigns** in the Pushwoosh Control Panel. 2. Select **Instant in-apps**. 3. Click **Create in-app** to set up a new campaign. ![Screenshot showing the Instant in-apps section with the Create in-app button highlighted](https://docs.pushwoosh.com/in-apps-sending-in-apps-7.webp) ## Define creative content ### Select an in-app banner In the **In-app banner** field, select an existing in-app template from the list, or create a new one with **Create new**. The list shows the most recently updated templates first. To change the order, click the sort button above it and choose **Newest first**, **Oldest first**, **Last updated**, **A-Z**, or **Z-A**. Pushwoosh remembers the order you pick and uses it in the in-app lists of one-time messages and journeys too. ![In-app banner field with Welcome Back selected, an arrow on the Sort: Last updated button, and the template preview with Edit content](https://docs.pushwoosh.com/in-apps-sending-in-apps-8.webp) #### Create an in-app template Click **Create new** and choose how to create the content: * **Create new template:** build the message in the built-in in-app editor. [Learn how to build the message](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/). * **Create native rich media:** build a native in-app message. Choose its display type in the editor. [Learn how to build the message](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/). * **Upload ZIP:** upload a ready-made HTML in-app message. For the ZIP format, see [Rich media templates syntax](https://docs.pushwoosh.com/developer/guides/content/rich-media-templates-syntax/). ![Create new menu open under the In-app banner field with Create new template, Create native rich media, and Upload ZIP](https://docs.pushwoosh.com/in-apps-sending-in-apps-29.webp) The two editors open full-screen over the campaign wizard. Save the template, and it’s selected in the **In-app banner** field. Then click **Back** to return to the wizard. If you close the editor with unsaved changes, Pushwoosh asks whether to close without saving. For **Upload ZIP**, enter a **Name**, select the archive in **ZIP File**, and click **Add template**. The template is selected right away, and no editor opens. Important A new template is not saved until you save it in the editor. Closing the editor before saving discards it. ### Preview the message On the right side of the screen, you’ll see a mobile preview of the selected in-app banner. To change the template, click **Edit content** next to **Preview**. It opens the template in the editor it was made in. **Edit content** isn’t available for templates uploaded as ZIP. Click **Next** to proceed to **Target users** settings. ## Define audience In **Target users**, set the event that triggers the message and choose which users should see it. ### Set a trigger event In **TRIGGER EVENT**, select the event that will launch the in-app message. Selecting a trigger is required. To make trigger conditions more specific, add event parameters: 1. Click **ADD ATTRIBUTE**. 2. Select values in **ATTRIBUTE**, **CONDITION**, and **VALUE**. 3. Add more attributes if needed. ![Screenshot showing event trigger configuration with event selection and attribute settings](https://docs.pushwoosh.com/in-apps-sending-in-apps-11.webp) ### Select which users will receive the in-app In **Which users should receive this in-app?**, select one option: * **All users with the mobile app**: Send the in-app to all app users. * **Users matching filters**: Limit delivery to users that match selected conditions. ![Target users section with options for all app users or users matching filters](https://docs.pushwoosh.com/in-apps-sending-in-apps-19.webp) #### Configure filters for target users If you select **Users matching filters**, use **COMBINE FILTERS** to configure targeting: ##### Match users by behavior Use this filter to show the in-app only to users who did or didn’t do an event a set number of times within a period. For example, show it to users who made a purchase in the last 30 days. 1. Turn on **Match users by behavior**. 2. In **USERS WHO** and **EVENT**, select whether users performed the event or didn’t, and which event. 3. Set how many times and during which period. ![Behavior filter settings with USERS WHO, EVENT, frequency, and timeframe fields](https://docs.pushwoosh.com/in-apps-sending-in-apps-16.webp) **Optional: target by a total.** To target users by the total of a numeric attribute, such as the amount spent, add a sum to the rule: 1. Click **Add sum** below the attribute rows. The button appears only if the event has integer or decimal (price) attributes. 2. In **Sum of**, select the attribute. The list shows only integer and decimal (price) attributes. 3. In **Condition**, select **is**, **is not**, **greater or eq**, **less or eq**, or **between**. 4. In **Value**, enter the total. You can’t go to the next step while it’s empty. ![Match users by behavior rule: performed Purchase exactly or more 1 time during the last 90 days, with Sum of amount greater or eq 100](https://docs.pushwoosh.com/in-apps-sending-in-apps-30.webp) How the sum is counted: * It covers all events that match the rule’s frequency, timeframe, and attribute conditions. * **performed:** users with no matching events aren’t included, whatever the value. * **didn’t perform:** the rule includes all users except those whose matching events reach the sum. For example, didn’t perform `Purchase` during the last 90 days with sum of `amount` greater or eq `100` includes users who spent less than 100 and users who made no purchases. * You can add one sum per attribute. A user matches only if every sum condition is met. For refunds, missing attributes, and time frame limits, see [how a sum is calculated](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/#how-a-sum-is-calculated). ##### Use existing segment Enable **Use existing segment** to select a saved segment from the list. You can click **Refresh** to update the segment size and **Edit segment** to review conditions. ![Existing segment filter with segment picker, refresh action, and segment conditions preview](https://docs.pushwoosh.com/in-apps-sending-in-apps-17.webp) ##### Use custom conditions Enable **Use custom conditions** to build a custom filter group by adding conditions. ![Custom conditions filter group with Country condition and Add Filter button](https://docs.pushwoosh.com/in-apps-sending-in-apps-18.webp) Note In this setup, choose either **Use existing segment** or **Use custom conditions** as the base audience source. ## Schedule the in-app message ### Configure display settings On the **Display settings** step, define the campaign period, timezone, and frequency capping. 1. In **START CAMPAIGN**, choose **Immediately** or **On chosen date**. 2. If you choose **On chosen date**, set the date and time. 3. In **END CAMPAIGN**, choose **Never** or **After chosen date**. 4. If you choose **After chosen date**, set the date and time. ![Display settings with START CAMPAIGN and END CAMPAIGN options for immediate or scheduled run](https://docs.pushwoosh.com/in-apps-sending-in-apps-20.webp) ### Set the timezone for campaign schedule In **Timezone for campaign schedule**, select the timezone used to interpret the campaign start and end dates and times. By default, the schedule uses **UTC-0 – Coordinated Universal Time**. Choose a different timezone if you want the start and end times to follow a specific local time. ### Configure frequency capping In **FREQUENCY CAPPING**, choose one option: * **Use global frequency capping settings**: Apply the global in-app channel limit configured in [Message delivery settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). * **Send messages without global frequency capping**: Ignore global capping limits for this in-app. * **Use custom frequency capping**: Apply campaign-specific limits instead of global thresholds. [See how custom and global capping interact](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/#how-custom-and-global-frequency-capping-work-together). Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping). #### Custom capping options | | | | ---------------------------------------------- | ------------------------------------------------------------------------ | | **Limited to total impressions** | Set the maximum number of times one user can see this in-app. | | **Limited to impressions in day(s)** | Set how many impressions are allowed during the selected number of days. | | **Enable day(s) interval between impressions** | Set a minimum interval between repeated impressions for one user. | ![Custom frequency capping settings with limits for total impressions, daily impressions, and interval](https://docs.pushwoosh.com/in-apps-sending-in-apps-22.webp) #### How custom and global frequency capping work together Important When custom frequency capping is enabled, only the custom limit applies. Global capping isn’t checked for this in-app and can’t block it. The in-app still counts toward the channel’s global counter, though. Example: * **Global limit for the channel:** 2 impressions a day. * **Campaign A’s custom limit:** 3 impressions a day. Because Campaign A has its own custom limit, it shows all 3 impressions today. The global limit of 2 doesn’t restrict it at all. But each of those 3 impressions still counts toward the global counter. Campaign B relies on global capping, with no custom limit of its own. Since the global count for today is already past 2, Campaign B won’t be able to show to that user at all today, because it was Campaign A’s impressions that used up the quota. [Learn more about Global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). ## Review and complete setup On the **Review** step, verify all campaign settings before launch. 1. Check **Content**, **Target users**, **Trigger event**, and **Display settings**. 2. Review the mobile preview on the right. 3. If needed, click **CHANGE OPTIONS** in any section to edit settings. ![Review step with campaign summary sections, change options links, preview, and Complete setup button](https://docs.pushwoosh.com/in-apps-sending-in-apps-23.webp) 4. Click **Complete setup**. 5. Select **Launch campaign** to activate the campaign or **Save as draft** to save settings without launching. In the **Confirm launching** window, confirm details and click the action you selected. After launch, the campaign appears in the **Instant in-apps** list with the current status, such as **Scheduled**. If you save the campaign as a draft, it appears in the same list with the **Draft** status. ## View and manage instant in-app campaigns Go to **Campaigns → Instant in-apps** to find campaigns, review performance, and edit, clone, resume, or delete them from one list. Use **Search by name or code…** to find a campaign by name or message code, or filter the list by **All**, **Active**, **Scheduled**, **Stopped**, **Draft**, or **Completed**. The list shows the most recently modified campaigns first. To change the order, use the sort menu next to the filters and choose **Last modified**, **Last created**, or **Name**. ### Monitor your campaigns Use the table to check whether a campaign is running and how it performs. Click the campaign name or the arrow to open details. * **Status:** The current campaign state. **Stopped** campaigns also show when they were stopped (for example, **Stopped on 27 May 2026**). * **Impressions:** How many times users saw the in-app. * **Interactions:** How many times users interacted with the in-app. * **Audience:** The number of user devices the in-app was displayed to (reach) over the last 30 days. * **Preview:** Hover over the eye icon to preview the in-app message without opening the wizard. ![Instant in-apps table showing Status, Impressions, Interactions, Audience, and preview icon columns](https://docs.pushwoosh.com/in-apps-sending-in-apps-28.webp) ### Take action on a campaign Click the **three-dot menu (⋮)** on a row to open the actions menu. The available actions depend on the campaign status. * **View statistics:** Open delivery and engagement metrics for the campaign. * **Edit campaign:** Open the campaign wizard and change content, target users, or display settings. * **Clone campaign:** Create a copy with the same creative, trigger event, audience, and display settings. Adjust settings in the wizard, then launch or save the copy as a draft. * **Resume campaign:** Relaunch a stopped campaign so it can deliver in-apps again. * **Change schedule:** Adjust the start and end dates for a completed campaign. * **Stop campaign:** End a running or scheduled campaign. You can update and relaunch it later. * **Delete:** Permanently remove the campaign. This action cannot be undone. Stop the campaign first if it is still running or scheduled. ## Example scenario: Sending an in-app message using in-app campaigns Imagine you run a fashion retail app and want to increase conversions with a limited-time discount when users engage with push notifications. You set up an instant in-app campaign for active users in the U.S. ### Prepare the in-app message [**Create an in-app template**](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) with a discount offer and a CTA button leading to checkout. ![Example in-app template with discount message text and CTA button](https://docs.pushwoosh.com/in-apps-sending-in-apps-24.webp) ### Configure target users Select `PW_NotificationOpen` as the trigger to catch users at peak intent, right after they engage with a push. Enable **Match users by behavior** and limit to users who triggered the event once in the last 5 days. This keeps the audience recently active without being too broad. Select `Active Users` as the base segment. ![Example target users setup with trigger event, behavior filter, and existing segment](https://docs.pushwoosh.com/in-apps-sending-in-apps-25.webp) ### Configure display settings Set a start and end date to keep the campaign time-bound. Set frequency capping to 1 impression per day with a 1-day interval to prevent the message from feeling intrusive if a user opens multiple push notifications in a short period. ![Example display settings with scheduled dates and custom frequency capping limits](https://docs.pushwoosh.com/in-apps-sending-in-apps-26.webp) ### Review and complete setup On the **Review** step, check all settings, click **Complete setup**, and select **Launch campaign**. ![Example review step with summary sections and Complete setup button](https://docs.pushwoosh.com/in-apps-sending-in-apps-27.webp) --- # Getting started with Kakao messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/kakao/ Send KakaoTalk messages via Customer Journeys or API. Includes setup steps, use cases, and prerequisites. --- KakaoTalk is a widely used messaging platform in South Korea and offers a powerful way to connect with your users. With Pushwoosh, you can send KakaoTalk messages as part of your marketing campaigns through Customer Journeys using pre-approved message templates. You can also send messages via the API using approved templates with variable substitution. ## Prerequisites for sending Kakao messages To start sending KakaoTalk messages with Pushwoosh, make sure the following requirements are met: 1. **Connect KakaoTalk to Pushwoosh** Link your KakaoTalk account to your Pushwoosh project by configuring your User Code and Secret Key. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration/) 2. **Create and approve message templates** Create KakaoTalk message templates in the Pushwoosh Control Panel and submit them for approval. Only approved templates can be used for sending messages. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) ## Create Kakao templates in Pushwoosh Create and manage KakaoTalk message templates directly in the Pushwoosh Control Panel. Templates must be approved before they can be used in campaigns. [Learn more ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/) ## Send Kakao messages You can send KakaoTalk messages in Pushwoosh using the following methods: ### Via Customer Journey Send KakaoTalk messages via Customer Journey using approved message templates with variable substitution for personalization. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/kakao/send-kakao-messages/#send-kakao-messages-via-customer-journey/) ### Via API Send KakaoTalk messages using the Pushwoosh API. Use approved templates and personalize them with variables. Developer assistance needed You’ll need your developer team’s help to send KakaoTalk messages via API. Share the guide below with them to get started. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/kakao/) --- # Send Kakao messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/kakao/send-kakao-messages/ Send KakaoTalk messages via Customer Journey or API to engage users, automate updates, and provide support. --- This guide walks you through how KakaoTalk messaging works in Pushwoosh and how to send KakaoTalk messages. ## Send Kakao messages via Customer Journey Pushwoosh allows you to integrate KakaoTalk messaging into [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Follow these steps to configure it: 1. Go to **Campaigns > Customer Journey Builder** in the main sidebar. 2. Click **Build a journey from scratch**. 3. Add an **Entry** element to define who enters the journey. [Learn how to configure the Entry elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) 4. Add a [**Kakao** element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/kakao/) and connect it to the rest of your flow (e.g., entry trigger and next steps). ![Kakao element added to Customer Journey canvas](https://docs.pushwoosh.com/kakao-send-kakao-messages-1.webp) 5. Click the **Kakao** element to open its settings panel. In the **Template** dropdown, select your approved message template. Templates are sorted by **Name A→Z** until you pick another order with the sort button above the dropdown. Note Only templates with APR (Approved) status are shown in the dropdown. ![Kakao template selection in Customer Journey element settings](https://docs.pushwoosh.com/kakao-send-kakao-messages-2.webp) 6. Use the variable selector to insert dynamic content: * Set **Variable type** (e.g., User property) * Choose a field like **firstName** * Set **Text case** (e.g., CapitalizeFirst) * Add **Fallback text** (optional): shown if the value is missing, for example, `there` * Click **Insert** to add the variable to your message ![Variable selector interface for personalizing Kakao messages in Customer Journey](https://docs.pushwoosh.com/kakao-send-kakao-messages-3.webp) 7. Click **Save** to save the element settings. 8. Complete the journey setup, ending each branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. When you’re ready, click **Launch journey** to activate it. ## Send Kakao messages via API Send KakaoTalk messages using the Pushwoosh API. Use approved templates and personalize them with variables. Developer assistance needed You’ll need your developer team’s help to send KakaoTalk messages via API. Share the guide below with them to get started. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/kakao/) --- # Getting started with LINE messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/line/ Send LINE messages via Customer Journeys, API, or chat. Includes setup steps, use cases, and prerequisites. --- LINE is a widely used messaging platform and offers a powerful way to connect with your users. With Pushwoosh, you can send LINE messages as part of your marketing campaigns through Customer Journeys, using either predefined templates or free-form text content. You can also send messages via the API or manage direct 1:1 conversations using the LINE Messages interface. ## What you can do with LINE messages With LINE messaging in Pushwoosh, you can: * Send promotional offers and product announcements to subscribed users. * Re-engage inactive users with personalized messages. * Notify users about order confirmations, delivery updates, or appointment reminders. * Trigger messages based on user behavior or external events. * Use LINE messages alongside other channels, such as push notifications, email, and SMS, to deliver unified, [omnichannel user experiences](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/). ## Prerequisites for sending LINE messages Developer assistance needed You’ll need assistance from your developer team to complete the configuration steps. Share the relevant links with them to get started. To start sending LINE messages with Pushwoosh, make sure the following requirements are met: 1. **Set up your LINE account**\ Create and configure your LINE account. You must have an active [LINE Official Business Account](https://www.linebiz.com/jp-en/) and a [LINE Developers Account](https://developers.line.biz/en/docs/line-developers-console/login-account/). 2. **Connect LINE to Pushwoosh**\ Link your LINE Official Business Account and **Messaging API channel** to your Pushwoosh project. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/) 3. **Activate and pay for LINE Messaging**\ Make sure LINE Messaging is set up and billing is enabled. **Important:** LINE Messaging API pricing is managed directly by LINE. Plans vary by region and usage volume. Refer to the [LINE Messaging API Pricing](https://developers.line.biz/en/docs/messaging-api/pricing/) page for details. ## Create LINE presets in Pushwoosh (optional) Create and preview LINE message content directly in the Pushwoosh Control Panel. [Learn more ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) ## Send LINE messages You can send LINE messages in Pushwoosh using the following methods: ### Via Customer Journey Send LINE messages via Customer Journey. Use message presets for rich content and formatting, or enter freeform text for quick, plain-text messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/line/send-line-messages/#send-line-messages-via-customer-journey/) ### Via API Developer help needed You’ll need your developer team’s help to set up and send LINE messages via the API. Share the API guide below with them to get started. Trigger LINE messages via the Pushwoosh API. This method supports both template-based and plain-text messages. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/line-api/) ### Send free-form messages and view conversations Use the LINE Messages interface to chat directly with users you’ve previously interacted with. You can view message history and send plain-text replies in a 1:1 format. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/line/send-line-messages/#send-free-form-messages-and-view-your-line-conversations/) --- # Send LINE messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/line/send-line-messages/ Send LINE messages via Customer Journey, API, or chat to engage users, automate updates, and provide support. --- This guide walks you through how LINE messaging works in Pushwoosh, the conditions under which messages can be delivered, and how to send LINE messages via Customer Journey or free-form chat. ## LINE message delivery conditions LINE messages can only be delivered under the following conditions: * The user has added your LINE Official Account as a friend. * Your LINE Official Account is part of a group or multi-person chat. * The user has messaged your LINE Official Account in a 1:1 chat within the last 7 days, even if they have not added you as a friend. When a message is sent, a **`200 OK`** status code may be returned even if the message is not delivered. Delivery will fail under the following circumstances: * The user has deleted their LINE account. * The user has blocked your LINE Official Account. * The user has not added your LINE Official Account as a friend. Important * Messages can only be sent using the user’s [**user_id**](https://developers.line.biz/en/docs/messaging-api/getting-user-ids/#what-is-user-id). **Phone numbers** cannot be used to send LINE messages. Refer to the official [LINE Messaging API documentation](https://developers.line.biz/en/reference/messaging-api/#send-push-message) for more details. ## Send LINE messages via Customer Journey Pushwoosh allows you to integrate LINE messaging into [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Follow these steps to configure it: ### Add an Entry element Begin by adding an **Entry** element to define who enters the journey. [Learn how to configure the Entry elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) ### Add and configure the LINE message element First, provide an internal name for the step (e.g., “LINE Welcome Message”). Next, choose one of the following: * **Preset:** Select from existing LINE message presets under **Content > LINE Presets**. The list starts with the newest presets. To change the order, click its sort button. [Learn more about creating LINE presets](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/) * **Custom content:** Enter plain text directly into the content field. ![LINE element Preset field with Weekend flash sale selected and an arrow on the Newest first sort button](https://docs.pushwoosh.com/line-send-line-messages-1.webp) Tip When using custom content, no additional settings are available. Users will only receive the plain text you enter here. To use advanced configuration, switch to the **Preset** option. ### Split flow depending on whether this message is delivered or not (optional) Toggle **Split flow depending on whether this message is delivered or not** to adjust the journey path based on delivery status: * **Enabled:** The journey will split into two paths, one if the message is delivered successfully, and another if it fails. * **Disabled:** The journey will follow the same path no matter whether the message is delivered or not. Click **Save** to apply the changes and return to the journey canvas. ![Split flow](https://docs.pushwoosh.com/line-send-line-messages-2.webp) ### Finalize and save the journey Complete the journey setup, ending each branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. When you’re ready, click **Launch journey** to activate it. ![Finalize and save the journey](https://docs.pushwoosh.com/line-send-line-messages-3.webp) ## Send free-form messages and view your LINE conversations Use the LINE Messages interface to view and manage direct conversations with users on the LINE platform. This inbox automatically displays users you’ve interacted with. You can view message history and reply in a 1:1 chat format. Messages sent through this interface are delivered as standard LINE chat messages and follow LINE’s delivery rules (e.g., the user must have contacted your LINE Official Account within the last 7 days if they haven’t added it as a friend). ![Free-form messages chat](https://docs.pushwoosh.com/line-send-line-messages-4.webp) ### Search for users Use the **Search** field at the top left to look up users by identifier. This helps you quickly find the conversation you want to continue. ### View message history Once you select a user from the list, their message history will appear in the center panel. This allows you to review past interactions before replying. ### Send a message To send a message: 1. Type your message in the **Message** field at the bottom of the screen. 2. Click **Send** to send the message. ![Send free-form messages](https://docs.pushwoosh.com/line-send-line-messages-5.webp) Tip This interface supports only plain text messages. For templates or rich messages, use LINE presets. ## Send LINE messages via API Developer help needed You’ll need your developer team’s help to send LINE messages via API. Share the guide below with them to get started. Use the Pushwoosh API to send LINE messages. This method supports both template-based and plain-text (free-form) messages. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/line-api/) --- # Marketing vs. transactional messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/ How message type affects opt-outs, frequency capping, and the control group across push, email, and in-app. --- When sending messages via Push, Email, In-App, SMS, WhatsApp, or Viber, you choose whether the message is **marketing** or **transactional**. This choice determines how Pushwoosh applies user preferences, frequency limits, and the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). ## Choosing the right message type Tip Use **marketing** for campaigns that can respect user choices and limits. Reserve **transactional** for essential, user-triggered or system-required communications. Using it for promotional content can lead to compliance issues and user complaints. | Message type | Marketing message | Transactional message | | ----------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | | **User preferences** | Respects subscription preferences. Only sent to users who opted in to the [subscription category](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#how-the-preference-center-works) you choose in the campaign. For SMS and Viber, not sent to numbers that opted out. See [marketing consent](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/). | Bypasses user preferences. Sent to all users, including those who unsubscribed from marketing messages. | | **Frequency capping** | [Global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) applies, except to [silent pushes and Live Activity updates](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/#which-messages-are-affected-by-global-frequency-capping). [Silence periods](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#silence-period) apply only in Customer Journey (not for Viber). | Frequency capping and silence periods do not apply. | | **Control group** | Excludes users in the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). | Includes users in the control group. | | **Email sunset policy** | Skips email contacts with no opens or clicks for the period set in the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts), if the project has it on. | The sunset policy does not apply. | | **Typical use cases** | Promotions and offers, newsletters, product and feature updates, re-engagement, lifecycle campaigns, announcements. | Account and security, orders and payments, critical notifications, verification. | ## Where to set the message type The message type is set in the channel-specific settings: either in a journey element or in the one-time message form. * **Email**: The message type is available in Customer Journey and when sending a one-time message. In a journey, set it in the [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-message-type). For one-time emails, set it in the [campaign setup](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#select-message-type). * **Push**: The message type is available in Customer Journey and when sending a one-time push. In a journey, set it in the [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#set-message-type). For one-time push, set it in the [campaign setup](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push/). * **In-app**: The message type is available only in Customer Journey. Set it in the [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/#set-message-type) when building a [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-in-apps-via-customer-journey/). * **SMS**: Set it in the [SMS element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#set-message-type) when building a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). * **WhatsApp**: Set it in the [WhatsApp element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#set-message-type) when building a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). * **Viber**: Set it in the [Viber element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#set-message-type) when building a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). This is separate from the Template/Freeform content choice, which only affects pricing. ### No preselected message type You choose the message type yourself: * **One-time push**: a new push has no message type selected. Choose one in the **Content** step to continue. * **Customer Journey**: new Push, SMS, Viber, WhatsApp, In-app, App Inbox, and Multi-Armed Bandit elements have no message type selected, and the element shows a **Message type not selected** warning until you choose one. Elements reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) show **Marketing message** with a **Default** badge. Choose **Transactional message** if the message is a service one. The **Message type not selected** warning in a journey element doesn’t stop you from applying the element or saving the journey. Email works differently. The one-time email form and the journey Email element don’t require you to choose a message type. ## API requests and message type Use the same **marketing** and **transactional** values in the API as in the Control Panel. For push, SMS, and WhatsApp, see [`/createMessage` parameters](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#message_type). For Viber, use `message_type` in [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). If you omit the type, the message is treated as marketing. For email, see [`createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage). --- # Message delivery settings **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/ Control how many messages users receive with frequency capping and send rate limits for push, email, SMS, WhatsApp, and App Inbox. --- Message delivery settings decide how many messages each user receives and how fast Pushwoosh sends them. Cap the number of marketing messages per channel so overlapping campaigns don’t overwhelm users, and limit the send rate so high-volume sends go out at a steady pace. Both are set per channel in **Settings > Message delivery settings**, and you can override them for individual messages. ## Configure message delivery ### Global frequency capping Limit how many marketing messages a user receives per channel within a set period, exclude specific users, and override the limit for individual messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) ### Send rate limits Set how many push notifications, emails, and SMS Pushwoosh sends per second, globally and for individual messages. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ### Global frequency capping FAQ Answers to common questions about how capping counts time, which messages it limits, and how setting changes affect campaigns. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/) --- # Global frequency capping **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/ Limit how many marketing messages a user gets per channel in a set period across push, email, in-app, SMS, WhatsApp, and Inbox. --- To prevent overwhelming your users with multiple messages from overlapping campaigns, use **Global frequency capping**. This feature limits the number of marketing messages a user receives within a specific timeframe. Once the message limit is reached for a channel, no further messages will be sent until the period resets. Update your API requests Set **message type** in API requests so frequency capping applies the way you intend. * **Push:** [`message_type`](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#message_type) in [`createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) requests * **Email:** [`email_type`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage) in [`createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage) requests **Transactional** sends are not subject to frequency capping. If you omit the type, capping applies. For the Control Panel, see [Marketing vs. transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). [Silent pushes](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications/) and [Live Activity](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) updates (iOS) are never capped, whatever their message type. See [Which messages are affected](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/#which-messages-are-affected-by-global-frequency-capping). ## Configure Global frequency capping To control the number of messages sent to a user within a specific timeframe: 1. Go to **Settings > Message delivery settings > Frequency capping**. ![Frequency capping settings tab in Message Delivery Settings](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-1.webp) 2. Toggle the switch on to enable frequency capping for the desired channels ([push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/), [emails](https://docs.pushwoosh.com/product/messaging-channels/emails/), [in-apps](https://docs.pushwoosh.com/product/messaging-channels/in-apps/), [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms/), [WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/), [App Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#app-inbox)). 3. Set the maximum number of messages per user for each channel and the corresponding time period. ![Message limit, period, and counting mode set for push, email, in-app, SMS, WhatsApp, and Inbox](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-2.webp) > For example, you can allow up to 4 push notifications, 2 emails, and 2 WhatsApp messages in a 5-day period. You can allow 1 to 1,000 messages per period, and the period can be 1 to 30 days. If you enter a value outside these ranges, the field changes it to the nearest allowed value, for example 1,500 messages to 1,000. 4. Choose a **Counting mode** for the channel: * **Rolling window** (default): counts messages sent within the last N×24 hours before each send. For example, with a cap of 3 pushes per 2 days, a push sent Monday at 9 AM stops counting on Wednesday at 9 AM, and a push sent Monday at 3 PM stops counting on Wednesday at 3 PM. * **Calendar day**: counts messages sent today and on the previous N-1 calendar days, starting at midnight. Select the **Capping timezone** (for example, `America/New_York`) that sets when midnight falls for this channel. For example, with the same cap of 3 pushes per 2 days, on Wednesday every push sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. If you don’t select a timezone, Pushwoosh counts calendar days in UTC. ![Counting mode section with Rolling window and Calendar day options and a Capping timezone picker](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-11.webp) Tip Calendar day suits teams that plan or report messaging by fixed daily periods rather than a rolling 24-hour window. Counting mode is also available wherever you can set a custom frequency cap: [one-time campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#set-frequency-capping), [instant in-apps](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/#configure-frequency-capping), and [customer journey message elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/). ### App Inbox To cap [App Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) messages, use the **Inbox** toggle in this list. App Inbox has its own counter, separate from push and every other channel. A user who has reached the push limit for the day can still receive an Inbox message, and the other way around. App Inbox has no send rate setting: users pick up Inbox messages one at a time when they open the app, so there’s no delivery spike to limit. ![Inbox toggle turned on with its own message limit and counting mode](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-12.webp) ### Exclude users from global frequency capping You can exclude specific users from frequency capping. Messages to excluded users bypass every limit on every channel, both the global limit and any custom limit set on a message. To exclude users from capping: 1. Go to **Settings > Message delivery settings > Frequency capping**. 2. In **Users excluded from frequency capping**, enter one [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) or one email at a time. 3. Click **Add to list**. 4. Repeat steps 2-3 for each additional user. 5. Click **Apply changes**. ![Users excluded from frequency capping section with User ID or email field and Add to list button](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-10.webp) Users remain excluded until you remove them from the list in message delivery settings. To clear the exclusions list, click **Clear exceptions list**, then click **Apply changes**. ## Global frequency capping for Journey messages Important Please note that to change the capping settings for active journeys, you will first need to pause the journey. When creating message elements in customer journeys, **Global frequency capping** settings are applied by default. ![Frequency capping indicator in journey message settings](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-3.webp) Note If no limit is set in Message delivery settings, the message will use **unlimited** global frequency capping. If needed, click the **Set up** link in the delivery settings to configure the limits. ![Delivery settings showing unlimited frequency capping and Set up link when limit is not configured](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-5.webp) However, you have the option to customize how certain messages are handled by overriding the Global frequency capping settings. You can do the following: ### Ignore Global frequency capping Select **Send messages without global frequency capping** to bypass global frequency capping rules. The user will receive this message regardless, even if they have exceeded the set message limits for the channel. The message also doesn’t count toward the channel’s global counter, so it doesn’t use up the quota of other messages. Use with caution to avoid sending too many messages. ![Delivery settings with Send messages without global frequency capping option selected](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-9.webp) ### Use custom frequency capping This option allows you to set a custom limit on the number of messages sent within a specified period. Here, define the number of messages (e.g., 5) and the time frame (e.g., 10 days) in which these messages can be sent. Choose a counting mode for this custom limit: [Rolling window or Calendar day](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping), same as for the global setting. Important When custom frequency capping is enabled, only the custom limit applies. Global capping isn’t checked for this message and can’t block it. The message still counts toward the channel’s global counter, though. Example: * **Global limit for the channel:** 2 messages a day. * **Campaign A’s custom limit:** 3 messages a day. Because Campaign A has its own custom limit, it sends all 3 messages today. The global limit of 2 doesn’t restrict it at all. But each of those 3 messages still counts toward the global counter. Campaign B relies on global capping, with no custom limit of its own. Since the global count for today is already past 2, Campaign B won’t be able to send to that user at all today, because it was Campaign A’s messages that used up the quota. ![Custom frequency capping](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-4.webp) Tip If you want to limit how often users can receive messages specifically from a journey (e.g., only once every 10 days), use [Campaign entry limits](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), not frequency capping. Entry Limits restrict how often users can enter a journey and receive its messages. ## Example scenario Imagine you’re managing multiple marketing campaigns across push notifications, emails, and SMS. To prevent overwhelming your customers with too many messages, you decide to set up Frequency capping and limit the number of messages each user can receive to 3 push notifications, 2 emails, and 1 SMS per user within 7 days. Here are the steps to set it up: ### Enable Global frequency capping 1. Go to **Settings > Message delivery settings > Frequency capping**. 2. Toggle the switch on for push notifications, emails, and SMS. 3. Specify the maximum number of messages a user can receive: 3 push notifications, 2 emails, and 1 SMS. 4. Specify the time period for 7 days. ![Screenshot showing frequency capping settings with 3 push, 2 emails, and 1 SMS per 7 days](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-6.webp) ### Configure critical messages If you want to make sure that important messages, such as payment confirmations, are always delivered immediately, regardless of any message limits, select **Send messages without global frequency capping** in the delivery settings of the specific journey steps where this is needed. ![Delivery settings with Send messages without global frequency capping option selected](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-9.webp) Examples of other critical messages can include security alerts, password reset requests, critical app updates, and more. ### Customize capping for specific messages Let’s say you want to send a special offer push notification every time a user completes a specific in-app action. However, to avoid repeating the same message too often, you want to ensure this message is shown no more than once every 3 days, even though your Frequency capping allows 3 push notifications per week. To configure it: 1. Set up a customer journey triggered by the user completing the target action. 2. Within the customer journey builder, go to the settings for the message element and select **Use custom frequency capping**. 3. Set the custom cap to 1 message every 3 days. Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the weekly budget shared with other push campaigns that rely on global capping. [See an example](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). ## View frequency capping statistics You can see how frequency capping affects your messages in several places. * **Project dashboard**: the [Project overview](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) shows a Frequency capping block on the Push and Email tabs for the selected period, 30 days by default. On the Push tab, the block follows the **All platforms** dropdown, so you can check capping for one push platform at a time. * **Individual message statistics**: the Overview tab of [push notification](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/) and [email](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) message statistics lists frequency capping as one of the Audience stage’s exclusion reasons. * **Aggregated messages**: the [Campaigns distribution](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#campaigns-distribution) funnel lists frequency-capped recipients as one of the Audience stage’s exclusion reasons, as a single count of unique recipients skipped. * **Customer Journey**: the [Communications statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#metrics-by-channel) window reports frequency capping on every channel tab. For answers to common questions, see [Global frequency capping FAQ](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/). --- # Global frequency capping FAQ **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/ Answers to common questions about how frequency capping counts time, which messages it limits, and how changes affect campaigns. --- ## How does Global frequency capping calculate time? It depends on the [counting mode](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping): * **Rolling window** (default): a day is exactly 24 hours from the time a message is sent. For example, if a message is sent on **January 12, 2024, at 01:00 AM**, the capping period applies until **January 13, 2024, at 01:00 AM**. * **Calendar day**: the period starts at midnight N-1 days before today, in the selected capping timezone, and runs to the current moment. For a 1-day cap, it’s since midnight today. For a 3-day cap, it’s today plus the two previous calendar days, not the last 72 hours. ## Which messages are affected by Global frequency capping? Global capping limits **marketing** messages. The following messages are not subject to frequency capping: * **Transactional messages:** messages sent with the [transactional type](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/), such as order confirmations and password resets. * **Silent pushes:** [push notifications with no alert or sound](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications/) that wake the app in the background. A push marked silent for at least one platform (iOS, Android, or Huawei) is exempt for every device it’s sent to, on all platforms. * **Live Activity updates (iOS):** messages that start, update, or end a [Live Activity](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) card on the lock screen. The rule applies across channels and sources, including API-based messages, Customer Journey messages, one-time push notifications and emails, in-app campaigns, SMS and WhatsApp messages, and App Inbox messages, which are counted separately from every other channel. ## Can I exclude specific users from Global frequency capping? Yes. In **Settings > Message delivery settings > Frequency capping**, add a `User ID` or email to **Users excluded from frequency capping**, then click **Apply changes**. These users bypass every frequency limit, both global and custom. ## What happens if I change Global frequency capping settings while a Customer Journey is running? The updated settings apply immediately. Pushwoosh checks the capping rules every time it attempts to send a message. Switching the counting mode doesn’t reset counters: Pushwoosh counts the messages already sent over the new period, starting with the next message it checks. ## What happens if I disable Global frequency capping? Future messages will no longer be limited by capping rules. However, messages that were previously blocked due to capping will not be sent retroactively. ## How does enabling Global frequency capping affect ongoing campaigns? Customer Journey messages and messages you create after the change follow the new rules right away. A one-time message that is already scheduled keeps the limits that applied when it was created. ## What if I enable Global frequency capping but still use the old capping settings in the Send Push form (legacy)? The system will continue using the old capping settings for messages sent via the legacy Send Push form. ## How does Frequency capping affect API-based messaging? **Marketing** API messages are subject to global capping. Set `message_type` to **transactional** in [`/createMessage`](https://docs.pushwoosh.com/developer/api-reference/messages-api/#createmessage) or `email_type` to **transactional** in [`/createEmailMessage`](https://docs.pushwoosh.com/developer/api-reference/email-api/#createemailmessage) so those sends are not capped. If you omit the type in the API request, frequency capping applies. Silent pushes and Live Activity updates are never capped, whatever their type. See [Which messages are affected](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping-faq/#which-messages-are-affected-by-global-frequency-capping). --- # Send rate limits **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/ Set how many push notifications, emails, and SMS Pushwoosh sends per second to control delivery speed during high-volume sends. --- Limit how many messages per second Pushwoosh sends on each channel, so high-volume sends go out at a steady pace instead of all at once. ## Set send rate limits Set individual send rate limits for each messaging channel: push notifications, emails, and SMS. To set up send rate limits: 1. Go to **Settings > Message delivery settings > Send rate**. 2. Enable or disable rate limiting for each channel as needed. 3. Specify the maximum number of messages to be sent per second to manage delivery speed and prevent backend overload during high-volume sends. Important Turning on **Send rate** for a channel affects all your current campaigns on that channel, including those sent via API. Review them before you apply the change. ![Send rate configuration screen showing push, email, SMS rate limits and toggles](https://docs.pushwoosh.com/messaging-channels-global-frequency-capping-8.webp) 4. Click **Apply changes**, then confirm to save your settings. ## Controlling send rate limits for individual messages In addition to setting a global send rate for each channel, you can also control the send rate for individual messages in customer journeys and one-time campaigns. This is done in the settings of the specific message or journey element. You can choose to: * Use global send rate settings * Send messages without global send rate * Use custom send rate For detailed instructions on configuring send rate per message, see the [channel-specific element guides](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/) and [one-time push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#set-send-rate-limits). --- # Getting started with message inbox **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-inbox/ Set up message inbox in your app, choose App Inbox or Save to Inbox for delivery, and track how users open those messages. --- ## What is message inbox? Message inbox is a space in your app or on your website where messages stay available after delivery. Users see them when they open the app or the page, not as a system notification. Inbox messages: * Do not show a push banner or alert on their own. * Do not change the app icon badge on their own. * Appear in the inbox list when the user opens the app or the page. Use message inbox when a message should remain readable later, for example when users opt out of push, dismiss a push, or you want content in the app without sending a notification. For silent pushes that update the app without an inbox entry, see [Silent push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications). Note Behavior on mobile and on web is not the same. See [How web message inbox differs from mobile](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/#how-web-message-inbox-differs-from-mobile). ## How web message inbox differs from mobile On web, the inbox reaches every visitor on a page where it’s enabled. Visitors don’t need to have accepted push notifications. Until a visitor logs in, their inbox history stays in that browser only. After they log in, that history moves to their account. If they clear site data, switch browsers, or switch devices before logging in, that history is lost for good and a later login won’t bring it back. Timestamps: on web, inbox times show in UTC. On mobile, they show in each user’s device timezone. Share the [Web message inbox](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) guide with your developers for setup. ## Set up message inbox Developer assistance needed You’ll need help from your developer team to configure message inbox. Share the guides below with them to get started. Before you send inbox messages, complete these steps: 1. **Configure your project** Set up platforms (iOS, Android, Web, and others) for your project. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) 2. **Embed the Pushwoosh SDK** Add the Pushwoosh SDK to your app or website, including the message inbox module. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Register users** Subscribe devices and sync user data so messages can reach the inbox. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) ### Implement message inbox in your app Share these guides with your developer team: [Mobile message inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) [Implement message inbox in mobile applications.](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox/) [Web message inbox ](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) [Set up message inbox for web applications.](https://docs.pushwoosh.com/developer/guides/message-inbox/web-message-inbox/) ### Customize the message inbox UI You can use the default message inbox UI or customize it to match your app. Share these guides with your developer team: [Customized iOS UI Guide ](https://github.com/Pushwoosh/pushwoosh-inbox-ui-ios-sdk) [Customized iOS XCFramework Guide ](https://github.com/Pushwoosh/PushwooshInboxUI-XCFramework) [Customized Android UI Guide ](https://github.com/Pushwoosh/pushwoosh-inbox-ui-android-sdk) Example This is an example of what the message inbox design can look like: ![Screenshot showing message inbox interface with list of notifications and message details](https://docs.pushwoosh.com/message-inbox-README-1.webp) ## Send messages to the inbox After message inbox is set up in the app, you can fill it in three ways. Pick the option that matches the campaign. ### Send a message only to Inbox App Inbox sends a message only to the inbox. It has its own content, audience, and schedule, and it does not send a push. [Send a message only to Inbox ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) ### Save a push to Inbox Save to Inbox turns on a toggle when you send a [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#save-message-to-inbox) or use a journey [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#save-to-inbox) element. The user gets the push, and a copy of that message also appears in the inbox. [Save a push to Inbox ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/) ### Send new feed items to Inbox A feed campaign sends every new item from an RSS or Atom feed to the inbox, to push, or to both, without manual sends. [Feed campaigns ](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) ## Track message inbox performance * To see how many people got and opened an App Inbox message, open the element’s statistics in the Control Panel. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/) * To see how a Save to Inbox push performed, check its push statistics. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/) * To count how many times users opened the inbox copy of a Save to Inbox push, use the API. [Learn more ](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog/) --- # Save a push to Inbox **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/ Copy a sent push into the in-app inbox with Save to Inbox, so users can revisit it later with a custom retention period and icon. --- Developer assistance needed Message inbox must be set up in the app. Share the [mobile Message inbox guide](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox) with your developers. **Save to Inbox** keeps a copy of your push in the app’s inbox when you send the notification. Users still get the push banner. If they dismiss it, miss it, or open the app later, they can find the same message in the inbox and act on it then. Typical use cases: * A promo or discount the user might dismiss and open later in the app * A reminder that should stay available for a few days after the push * Any push that still matters after the banner is gone Tip If you need an inbox message without sending a push at all, use [App Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) instead. Inbox timestamps use each user’s device timezone. ## Prerequisites Before Save to Inbox works: * Message inbox must be set up in the app. Share the [mobile Message inbox guide](https://docs.pushwoosh.com/developer/guides/message-inbox/mobile-message-inbox) with your developers. * For the product overview and setup path, see [Getting started with Message inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/). ## Turn on Save to Inbox Turn the toggle on when you create the push. The Control Panel label depends on where you send: * One-time messages: **Save message to Inbox** * Customer Journey Push element: **Save to inbox** ### One-time push Use this path when you send a one-time push from Campaigns. 1. Go to **Campaigns > One-time messages**. 2. Start a [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/). 3. On the **Content** step, turn on **Save message to Inbox**. The inbox title and message come from the push preset. Then set retention, icon, and tap action in [Set inbox options](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/#set-inbox-options). ### Customer Journey Use this path when you send the push from a journey. 1. Open your journey and select the [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element on the canvas. 2. In the element settings, turn on **Save to inbox**. ![Save to inbox toggle enabled in push settings](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-1.webp) 3. Set the inbox options below, then click **Apply**. ## Set inbox options After you turn the toggle on, set how the inbox copy looks and behaves. ### Remove from Inbox In **Remove from Inbox**, choose how long the message stays: * A number of days (for example, 3 days). * A specific date. The maximum retention period is **30 days**. ![Remove from Inbox dropdown with retention period options](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-2.webp) ### Message icon Set an icon URL so the message is easy to spot in the inbox list. If you leave it empty, the default icon is used. ![Icon URL field for the inbox message](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-3.webp) ### Action when inbox clicked Choose what happens when the user taps the inbox row: * **Use the action you set in the push content:** same deep link, URL, or screen as the push. * **Open in-app banner:** open an [in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) you select. Create the [Rich media](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) first, then pick it from the list. ![Action when inbox clicked options](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-4.webp) ## Example scenario Send a short-lived promo push that stays in the inbox for four days, then opens an in-app with the promo code. 1. Create a journey with [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) for your loyal customers segment. 2. Add a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element and set the promo title and message. ![Preset selected with preview content bubble showing the promotional message](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-5.webp) 3. Toggle **Save to inbox**. In **Remove from Inbox**, set **4 days**. ![Save to inbox enabled with 4-day retention](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-6.webp) 4. Optionally set an icon URL (for example, your store logo). ![Custom icon URL for the inbox message](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-7.webp) 5. Under **Action when inbox clicked**, choose **Open in-app banner** and select the Rich media that shows the promo code. ![Open in-app banner selected with Rich media dropdown](https://docs.pushwoosh.com/message-inbox-mobile-message-inbox-8.webp) 6. Click **Apply**, then finish the rest of the journey. ## Track inbox message statistics The Control Panel doesn’t show inbox open rate for a Save to Inbox push. Pull it with the [`getMessageLog`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#getmessagelog) API method, filtering on `actions: ["inbox_opened"]`. For everything the Control Panel does show for this push, see [message statistics for a push](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/). To see what one user currently has in their inbox, open [User Explorer → Inbox](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#inbox-tab). --- # Send a message only to Inbox **URL:** https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/ Send a one-time App Inbox message with its own content, audience, and schedule, without relying on any push notification. --- **App Inbox** sends a message only to the in-app inbox. It does not send a push. The message appears when the user opens the app, so they can read it later even if they never saw a notification. Use App Inbox when you want inbox content on its own, or when users might miss or ignore push. Tip If you send a push and also want a copy in the inbox, use [Save a push to Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/) instead. The app needs the Message Inbox SDK module before users can receive Inbox messages. Note The card layouts need Web SDK 3.75.0, Android SDK 6.12.0 with the `pushwoosh-inbox-ui` module, or iOS SDK 7.0.40 or later with the PushwooshInboxKit module, and the **Carousel** layout needs iOS SDK 7.1.2 or later. An older SDK still receives the message but shows it without the layout. Devices on a platform without an inbox don’t receive Inbox messages. To send a one-time Inbox message: 1. Go to **Campaigns > One-time messages**. 2. Click **Send message**. 3. Choose **Inbox message**. ![Send message dropdown on One-time messages showing Inbox message between Push notification and Email](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-1.webp) ## Content ### Select or create App Inbox content Search for an existing [App Inbox preset](https://docs.pushwoosh.com/product/content/app-inbox-presets/), or click **Create new** to build content inline. Either way, the content editor is the same one used for [App Inbox presets](https://docs.pushwoosh.com/product/content/app-inbox-presets/#choose-a-layout-type). Pick a layout type, then fill in the fields for that layout. Note Content you build inline with **Create new** isn’t added to the [App Inbox presets](https://docs.pushwoosh.com/product/content/app-inbox-presets/) list. To reuse content across several messages, create the preset from **Content > Inbox content** first, then search for it here. To change the order of presets in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every App Inbox preset dropdown, in one-time messages and journeys alike. ### Preview the message The **Preview** panel next to the form shows the message as an inbox cell. Choose **iOS**, **Android**, or **Web** above the cell to see how it looks on that platform. **Web** shows the cell as the Web SDK inbox widget draws it on your site. There’s no Huawei option, because Huawei devices use the [Pushwoosh Android SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/huawei-integration/). Choose **Android** to preview them. ![Preview panel with Web selected, showing the Weekend flash sale message as a Web SDK inbox widget cell](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-8.webp) The preview platform doesn’t affect who gets the message. The platforms that receive it are set in **Target platforms** on the [Audience](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/#select-audience-for-the-inbox-message) step. To check the message on a real device first, click **Test app inbox message** under the preview. It sends the current content to the app’s [test devices](https://docs.pushwoosh.com/developer/first-steps/test-your-integration/test-devices/). See [Send a test message](https://docs.pushwoosh.com/product/content/app-inbox-presets/#send-a-test-message). If the content has several languages, use the chips under the cell on the **Review** step to switch between them. ### Choose a tab If the app’s inbox is split into [tabs](https://docs.pushwoosh.com/product/content/app-inbox-presets/#set-up-inbox-tabs), choose one in **Tab**. The message shows in that tab and in **All**. Leave **No tab** to show it only in **All**. Tabs show in the inbox of iOS SDK 7.3.0 and Android SDK 6.17.0 or later. The Web SDK inbox has no tabs. ![Tab field set to Offers with the hint that the message shows in this tab and in All](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-9.webp) If the app has no tabs yet, the field shows **Set up tabs** instead, which opens the tab settings. ### Select message type Choose **Marketing message** or **Transactional message**: * **Marketing message:** respects frequency capping. Excludes the [global control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message:** bypasses frequency capping. Sent to the global control group too. ![One-time Inbox Content step with Weekend flash sale preset selected and Marketing message chosen](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-2.webp) ## Audience ### Select audience for the Inbox message Choose who receives the message: * **Send to segment:** users in a saved segment. * **Send to specific users:** named User IDs or HWIDs. * **Send to all subscribers:** everyone with a registered device for the selected platforms. In **Target platforms**, leave iOS, Android, Huawei, and Web selected, or narrow the list to the platforms you need. ## Sending time ### Choose when to send * **Immediately**: the message is placed in the inbox as soon as you send. * **On chosen date**: schedule the message for a specific date and time. ![Send date field set to Immediately](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-3.webp) In **Timezone for campaign schedule**, choose the timezone used to interpret **On chosen date**. ![Timezone for campaign schedule field set to UTC](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-4.webp) ### Set expiration In **Expires**, choose how long the message stays in the inbox: * **After 7 / 14 / 30 / 90 days**, or a specific date. * Leave **After 30 days** unless you need a shorter or longer window. The default is longer than the retention on the [Save to Inbox toggle](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/#remove-from-inbox) (max 30 days), because an Inbox message needs to survive until the user’s next app open, not just until the next day. ![Expires field set to After 30 days](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-5.webp) ### Set frequency capping In **Frequency capping**, choose how often this user can get App Inbox messages. Limits apply to the [App Inbox frequency capping channel](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#app-inbox), separate from push, email, and other channels. * **Use global frequency capping settings:** apply the project-wide App Inbox limit. * **Send messages without global frequency capping:** send even if the user already hit that limit. Use with caution. * **Use custom frequency capping:** set a limit only for this message. Note Users open the app on their own schedule, so Send rate and Time to live aren’t used for App Inbox. ![Frequency capping field showing global settings and the unlimited fallback note](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-6.webp) ## Actions On the **Actions** step, set **On click actions** for each platform. **Mobile:** * **No action on click:** nothing opens when the user taps. * **Open Deep link:** open a deep link in the app. * **Open URL in browser:** open a URL in the device browser. * **Show in-app content:** show an [in-app](https://docs.pushwoosh.com/product/messaging-channels/in-apps/) message you select. The in-app list is sorted by **Oldest first** until you pick another order with its sort button. **Web:** * **No action on click:** nothing opens when the user taps. * **Open URL in browser:** open a URL in the browser. * **Show web pop-up:** show a [web popup](https://docs.pushwoosh.com/product/messaging-channels/web-popups/) you select. The list starts with **Recently updated** until you pick another order with its sort button. Pick web content here, not a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). The Web SDK shows a web form only inside a web campaign, so a web form picked here doesn’t open. ## Review ### Review and send 1. Review the summary. If you chose a tab, it shows next to **Tab:**. 2. Click **Send now**. If you set a specific date in Sending time, this button reads **Schedule send** instead. 3. In the dialog that opens (**Confirm sending**, or **Confirm scheduling** if you scheduled), set **Campaign name**: a free-text label for this one message. It’s prefilled with the Inbox preset’s own title, or its preset name if the preset has no title. Clear it and Message History falls back to the same default. Keep it to 255 characters or fewer. Typing past the limit truncates the text without a warning. ![Confirm sending dialog with Campaign name field prefilled from the Inbox preset's title](https://docs.pushwoosh.com/shared-64.webp) Once set, the campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the preset title. 4. Click **Send** to confirm, or **Schedule send** if you’re scheduling. ![Review step for one-time Inbox message with summary and Send now](https://docs.pushwoosh.com/message-inbox-send-app-inbox-messages-7.webp) Caution A scheduled message keeps the tab you chose when you scheduled it. Pushwoosh doesn’t check the tab again on the send date, so if you delete the tab before then, the message still goes out and shows only in **All**. To keep the message in a tab, edit it and choose another tab before you delete the old one. To change or clear the campaign name, or to cancel a message that’s still scheduled, open **Campaigns → One-time messages** or [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#edit-or-cancel-scheduled-one-time-messages). The actions menu (⋮) there covers Inbox sends the same way as other channels. ## Send from a journey Use a journey when the Inbox message should follow a trigger, wait, or another channel. Skip the journey if you only need a one-time send. 1. Open a journey in Customer Journey Builder. 2. From **Channels**, drag **App Inbox** onto the canvas where you want the message. 3. Open the element and set content, tab, message type, on-click actions, expiration, and frequency capping the same way as in the one-time flow above. Full setup: [App Inbox journey element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/). --- # Getting started with push notifications **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/ Learn how to maximize your campaigns with push notifications. Set up platforms, create content, and send targeted messages to engage users. --- Push notifications are a powerful communication tool that delivers instant, personalized messages directly to users’ devices, attracting their attention and inviting real-time interaction. You can use push notifications independently or in combination with other channels like email, in-app messages, SMS, or WhatsApp. ## What can you do with push notifications? **Boost sales and conversions** * Send [personalized notifications](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/) based on product views or purchase behavior, offering tailored recommendations, promotions, or discounts. * Implement abandoned cart reminders to nudge users into completing their purchases. **Increase user engagement and retention** * Send timely updates about new features, content, or personalized recommendations. * Notify users about achievements, challenges, or rewards to keep them engaged. **Strengthen brand loyalty** * Share informative content beyond promotions—industry news, tips, or educational materials. * [Celebrate user milestones](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-anniversary-campaigns/) (e.g., birthdays, anniversaries) with personalized notifications. ## Prerequisites for sending push notifications Developer assistance needed You’ll need assistance from your developer team for these steps. Share the links below with them to get started. To send push notifications using Pushwoosh, you need to complete the following steps: 1. **Configure your project** Set up platforms (iOS, Android, Web, etc.) for push notifications. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/) 2. **Embed the Pushwoosh SDK** Integrate the Pushwoosh SDK into your app or website. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview/) 3. **Start subscribing users** [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) ## Create push content Craft compelling push notifications that capture user attention and drive engagement. Ensure your messages are clear, relevant, and personalized for maximum impact. [Learn more about creating push content ](https://docs.pushwoosh.com/product/content/push-presets/) ### Personalize push notifications for better engagement Enhance user experience by sending personalized push notifications tailored to user behavior and preferences. [Learn more about how to send a personalized push ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-push-notification/) ## Send push notifications Once your setup is complete, you can send push notifications in several ways. Some options require additional developer configuration. * **[One-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/)**: Send a single notification to your audience. * **[One-time push via Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/)**: Send a one-time push from a Customer Journey. * **[Recurring push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes/)**: Automate messages at regular intervals. * **[Scheduled push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/scheduled-push/)**: Predefine a specific time to send notifications. * **[Targeted push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/targeted-push/)**: Send push messages based on user segmentation. * **[Push to all user devices](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/push-by-user-id/)**: Send notifications to all devices associated with a User ID. Developer assistance needed Push to all user devices and some other options require additional configuration. Share the links above with your development team for setup details. ## Subscription prompt Implement a prompt in your app to request user permission for push notifications. * **[Subscription prompt](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/)**: Overview and options. * **[Default widget](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/default-widget/)**: Use the default widget to trigger the native subscription prompt. Developer assistance needed Requires developer assistance. Share these links with your team. ## Advanced push features Extend push notifications with silent delivery, interactive buttons, deep links, custom data, and iOS-specific features. Each link below covers setup and configuration. Developer assistance needed Some of these features require additional developer setup. Share the links below with your team. * **[Silent push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications/)**: Deliver push messages without user-visible alerts. * **[iOS interactive push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-interactive-push/)**: Add actionable buttons to iOS push notifications. * **[Deep links](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/)**: Direct users to specific in-app screens when they tap a notification. * **[Using custom data](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data/)**: Pass custom data in push payloads to personalize experiences. * **[iOS Live Activities](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities)**: Display real-time updates on the iPhone or iPad Lock Screen and Dynamic Island. * **[iOS custom foreground push](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-custom-foreground-push-notification)**: Display custom in-app notifications with animated banners, haptic feedback, and Liquid Glass effects when the app is in the foreground. ## Geo campaigns Reach users when they are near relevant places and control message frequency to avoid sending too often. * **[Geo campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/)**: Learn how location-based triggers work and how to set up geozones and clusters. ## Track and analyze push notification performance Pushwoosh provides comprehensive analytics to help you measure push notification effectiveness and optimize your messaging strategy. * **Per push statistics**: Track delivery rates, open rates, and user interactions. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics) * **Campaign-level insights**: Analyze push notification performance within Customer Journeys alongside emails, in-app messages, and SMS interactions. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#communications) ## Import push subscribers Migrating from another push provider? Import your existing push subscriber base from a CSV file, without asking users to reinstall the app. [Learn more about importing push subscribers ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/import-push-subscribers/) ## Manage your push subscriber base Keep your push campaigns efficient by regularly removing inactive or invalid push tokens. [Learn more about managing your push subscriber base ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/manage-push-subscriber-base/) --- # Understanding advanced push features **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ Use silent push, interactive buttons, deep links, custom data, and geo-based targeting to enhance your push campaigns. --- Extend push notifications with features that require additional setup or developer configuration. #### Silent push Deliver push messages without user-visible alerts for background updates and data sync. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications/) #### iOS interactive push Add actionable buttons to iOS push notifications for quicker user responses. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-interactive-push/) #### iOS push stories Turn an expanded iOS push notification into a full-screen, Instagram-style stories experience. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/) #### Deep linking Direct users to specific in-app screens when they tap a notification. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) #### Custom data Pass custom data in push payloads to trigger in-app actions and personalize experiences. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data/) #### iOS Live Activities Display real-time updates on the iPhone or iPad Lock Screen and Dynamic Island. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/) #### iOS custom foreground push Display custom in-app notifications with animated banners, haptic feedback, and Liquid Glass effects when the app is in the foreground. [Learn more ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-custom-foreground-push-notification/) --- # Deep links **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/ Use deep links in push campaigns to direct users to targeted in-app content and maximize campaign effectiveness. --- By default, tapping a push notification opens your app’s main screen. Deep linking allows you to take users directly to a specific part of your app when they tap on a push notification. This can be a product page, a feature, or any other location within your app. A deep link leads inside your app. To send users to a website or another app, such as a YouTube video, you don’t need one. Put a regular `https://` address into the push’s on-click action. See [Open a YouTube video or another app](https://docs.pushwoosh.com/product/content/push-presets/#open-a-youtube-video-or-another-app). ## Benefits of deep linking * **Increased engagement**: Users are more likely to complete desired actions. * **Improved user experience**: Users are taken directly to the relevant content. * **Higher conversion rates**: By streamlining the user journey, you can boost sales or sign-ups. ## Use cases * Direct users to a specific product page or sale event within the app, increasing the chances of conversion. For example, you can send a notification saying, “Flash Sale! 50% off on Electronics,” that takes users directly to the sale section in the app. * Use deep links to send users to content tailored to their interests or past behavior, enhancing engagement. * Bring inactive users back to the app by linking them directly to new features or updates that might interest them. ## How to implement deep links ### Create deep links In Pushwoosh Control Panel, open your App and go to the **Deep Links** section, then click **Add Deep Link**. Enter the name of the deep link and its template. The template follows standard URI scheme which is **scheme://[domain]/path[?query]**. The scheme must be unique to your application. You can also reference additional parameters that could be entered later by using `{placeholders}` in curly braces. You can use several placeholders in your deep link template. The example below illustrates a deep link with the scheme `com.pushwoosh`, page name `promotion`, and query parameter `id` with placeholder `promo_id`: ![Deep link configuration screen showing scheme, page name, and query parameter with placeholder](https://docs.pushwoosh.com/push-notifications-deep-linking-1.webp) ### Configure deep links Developer assistance needed To implement deep linking effectively, your development team will need to set up and configure these links within the app. For detailed technical guidance, please refer to [the Developers’ Guide on implementing Deep Links](https://docs.pushwoosh.com/developer/guides/messaging-channels/deep-linking). ### Use deep links To link to a specific page within your app while creating push content, go to the **On-click actions** section of the Push Content form and configure a deep link. For detailed steps, [see this guide](https://docs.pushwoosh.com/product/content/push-presets/#define-the-next-step-for-users-who-click-on-the-notification). --- # iOS interactive push **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-interactive-push/ Enhance user experience by adding actionable buttons to iOS push notifications for faster and more effective interactions. --- iOS Interactive Push Notifications offer a dynamic way to engage users by allowing them to interact directly with notifications. Unlike standard push notifications that simply open the app, interactive notifications provide actionable options within the notification itself. This increased interactivity boosts user engagement by enabling swift responses, such as replying to a message or completing a task. By offering valuable quick actions, you can significantly enhance user experience and foster deeper connections with your app. ## Use cases ### E-commerce * Notify users of a sale and provide actions like “Shop Now” or “Save for Later.” * Send abandoned cart reminders with options like “Complete Purchase” or “View Cart.” ### Media & Entertainment * Send notifications about new content with actions like “Watch Now” or “Add to Playlist.” ### Gaming * Alert players to special in-game events with actions like “Join Now” or “View Details.” * Daily rewards: Notify users of daily rewards with options like “Claim Now” or “View Rewards.” ## Creating categories in Pushwoosh Control Panel To implement these interactive elements, iOS utilizes notification categories. These categories group the available actions within a notification, defining the options users can select. Pushwoosh simplifies this process by allowing you to create and manage these categories directly from the Pushwoosh Control Panel. For the step-by-step configuration flow, see [iOS action buttons configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-action-buttons-configuration/). Your development team will also need to handle button taps in the app’s notification delegate — share the [Developers’ Guide on iOS Interactive Push Notifications](https://docs.pushwoosh.com/developer/guides/messaging-channels/ios-interactive-push) with them. --- # iOS push stories **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/ Turn an expanded iOS push notification into a full-screen, Instagram-style stories experience with auto-advancing pages. --- Push stories turn an expanded iOS push notification into a full-screen, Instagram-style stories experience: full-bleed images, auto-advancing pages, and a button on each page. ## Prerequisites Before you send push stories, your iOS team needs to: 1. Add a Notification Content Extension to the app using the `PushwooshNotificationUI` module. Share [iOS push stories](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-stories/) with them. 2. Release an app update that includes the extension. Note Push stories render only on iOS. Recipients who don’t have that app update yet see a regular push with the title, subtitle, and message from the preset, without the Stories images. ## Create push stories content Build the pages shown once the user expands the notification in a push preset. See [Build push stories content](https://docs.pushwoosh.com/product/content/push-presets/build-push-stories-content/). ## Send a one-time push with push stories 1. Go to **Campaigns** → **One-time messages** → **Send message** → **One-time push**. 2. [Select or create](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#select-or-create-push-message-content) **Push stories** content. 3. Set up the audience, schedule, and other settings the same way as for a regular [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/). Because stories play only on iOS, the **Target platforms** list on the Audience step locks to iOS, and the other platforms are unavailable for this send. ![Target platforms on the Audience step narrowed to iOS only for a push stories send](https://docs.pushwoosh.com/ios-push-stories-target-platforms.webp) ## Send push stories in a journey Select **Push stories** content in the [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#send-push-stories) of a journey. The element shows the app requirements for stories in a banner. ## References [iOS push stories (developer guide) ](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-push-stories/)Add the Notification Content Extension and PushwooshNotificationUI module to your iOS app. [Build push stories content ](https://docs.pushwoosh.com/product/content/push-presets/build-push-stories-content/)Field-by-field setup for the Stories pages in a push preset. [One-time push ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/)Send a one-time push notification, choose content, audience, and schedule. --- # Silent push notifications **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/silent-push-notifications/ Learn how to send silent push notifications from your app without notifying users for background updates and data synchronization. --- There are situations where you may need to update your app, pass data, or retrieve new content from your server without notifying the user or displaying any visible alerts. Silent push notifications are designed for exactly this purpose. Silent push notifications are delivered to users’ devices with no alert or sound. The app icon badge isn’t suppressed. A silent push can still set it if the payload carries a badge value. Only auto-incrementing the badge doesn’t work, since that relies on code that only runs for pushes with an alert. When a silent push arrives, the app is woken up in the background. Users don’t recognize any alerts and don’t see any push content. By sending silent push notifications, you can: * Notify your app about new content available to download * Perform some tasks in the background * Get new data from your server * Pass custom data to the app Silent push notifications also come in handy when you need to clean up the user base. Every time a push is sent, all invalid or non-existent push tokens are removed from our database. That’s why silent push notifications are used in [Uninstalls tracking](https://help.pushwoosh.com/hc/en-us/articles/360033774692-Uninstalls-Tracking), which keeps your user base valid and updated. ## Implementation ### Customer Journey You can send silent push notifications using the [**Data to app**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) element in Customer journeys. The **Data to app** element automatically sends silent push notifications with [custom data](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data/) to trigger in-app actions without alerting users. To send silent push notifications: 1. Add the **Data to app** element to your [Journey canvas](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). 2. Paste your JSON code into the provided field. The JSON payload contains the custom data your app will process when the silent push is received. 3. Click **Save** to apply the configuration. The silent push notification will be delivered to users’ devices in the background, waking up your app to process the custom data without displaying any alerts or sounds. The app icon badge can still change if the payload sets one. [Learn more about the Data to App element ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) ### API You can also send silent push notifications via the Pushwoosh API with the assistance of your development team. [Learn more about sending silent push notifications via API ](https://docs.pushwoosh.com/developer/guides/messaging-channels/silent-push-notifications/) --- # Using Custom Data **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/using-custom-data/ Drive engagement through personalized push notifications using custom data to trigger actions, grant rewards, and refresh content. --- Pushwoosh allows you to customize your push notifications by adding custom data. This enables you to personalize the user’s experience by triggering specific actions within your app when a user interacts with a push notification. ## What is custom data? Custom data refers to additional commands or parameters included in your push notifications, typically formatted in JSON. This data can control how your app responds when users open the notification, such as displaying a custom message, changing the app layout, or offering personalized discounts. ## How custom data works When a push notification with custom data is received, the app processes the JSON payload to perform predefined actions. This allows for dynamic content updates, navigation, and other interactive features without requiring the user to manually open the app. ## Example use cases * **Personalized messaging**: You can send users personalized messages by including custom data such as discount offers or event reminders. * **App personalization**: Custom data can change elements of your app, like colors, images, or even app icons, based on user preferences or campaign targets. * **Updating your application proactively**: In certain situations, you may need to update elements of your application on users’ devices, such as changing icons, colors, or enabling specific sections. These updates don’t involve downloading a new version of the app but rely on functionality already built into the application. * **Granting bonus points to users**: You can send push notifications with custom data to grant bonus points to users. When the user taps on the notification, the bonus points are automatically credited to their account. ## How to include custom data You can add custom data while [creating a push notification in the Control Panel](https://docs.pushwoosh.com/product/content/push-presets#add-custom-data) or by using the [Data to App journey element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/). Developer assistance needed While adding custom data in the Pushwoosh Control Panel is straightforward, implementing this data within your app requires developer assistance. Developers will need to integrate the custom data handling into your app to trigger specific actions when users interact with notifications. Please refer to the [Developer Documentation](https://docs.pushwoosh.com/developer/guides/messaging-channels/using-custom-data) for the technical steps involved. --- # Understanding geo campaigns **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/ Learn how geo campaigns trigger by location, then create geozones and clusters, import them in bulk, and manage everything. --- A geo campaign sends a push notification, fires a custom event, or both automatically when a user’s device enters a location you defined on a map, such as a store, a venue, a delivery point, or a transit hub. Pushwoosh uses geozones to define where a campaign can trigger. You can group multiple geozones into a [cluster](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/) to apply one shared cooldown and avoid repeated sends in nearby areas. ## How it works After you set up a geo campaign, Pushwoosh runs the entry actions you enabled for the geozone ([**Send push**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#send-push), [**Fire event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-event), or both) when a user’s device enters the geozone, the campaign is active, and [cooldown](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#choose-a-cluster-and-cooldown) allows delivery. Cooldown applies to both entry actions. It blocks a repeat entry until the cooldown expires, either from that geozone alone or from every geozone in its cluster if it belongs to one. [**Fire exit event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-exit-event), [**Fire dwell event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-dwell-event), and [**Assume exit after silence**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#assume-exit-after-silence) are not limited by cooldown. Those actions track that someone left the zone or stayed inside. If cooldown also blocked them, you would miss the signal that the visit ended. Location permission required Location permission is required. If a user has not granted it, geo campaigns cannot trigger for that device, and no entry action runs when the device enters a geozone. On Android 10 and later, background location permission is required. ## Use cases * Send an in-store offer when a user enters a geozone around a retail location. * Trigger an arrival message at a venue, pickup point, or branch office. * Limit repeat sends in dense areas by applying one cooldown across a cluster of nearby geozones. ## Prerequisites Developer assistance needed You’ll need help from your development team to integrate location tracking in your app. Before you create your first geo campaign, make sure: 1. To send pushes from geozones, at least one mobile platform (Android or iOS) is configured in the project. Geozones that only fire events work without it. [Learn how to configure platforms](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/). 2. The Pushwoosh SDK is integrated in your app with location tracking enabled. 3. Location tracking is configured in the app. For implementation details, see [Geozones for iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/customizing-ios-sdk/#geozones-push-notifications) and [Geozones for Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/customizing-android-sdk-50/#geozones-push-notification). Note * Geo-based messaging is not available for HMS (Huawei Mobile Services) devices, since it relies on Google location services. * For Google Play apps, approval is required for background location access. Refer to the [Google Play policy](https://support.google.com/googleplay/android-developer/answer/9799150#zippy=%2Cstep-review-best-practices-for-accessing-location) and submit the [permissions declaration](https://support.google.com/googleplay/android-developer/answer/10144311) before collecting users’ locations. ## Set up a geo campaign [Create a geozone ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/) [Configure entry actions, the push message, the trigger shape, cooldown, and the campaign schedule.](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/) [Create a cluster ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/) [Group geozones that share a cooldown, then assign zones from the form or the Geo campaigns list.](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/) [Import geozones from a CSV file ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/import-csv/) [Bulk-create geozones from a spreadsheet instead of placing them on the map one at a time.](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/import-csv/) [Manage geozones and clusters ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/) [Pause, edit, move, or delete geozones and clusters.](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/) --- # Create a cluster **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/ Group geozones that share a cooldown so nearby stores or pickup points do not send repeat pushes to the same customer device. --- Use a cluster when the same device might enter several of your zones in a short window, for example, two stores on the same street. Once a device receives a push from any geozone in the cluster, it stays silent for all others in that cluster until the cooldown expires. After you create the first geozone, you can create clusters from the **Geo campaigns** page. 1. On the **Geo campaigns** page, click **Create cluster**. ![Geo campaigns page with the Create cluster button before entering cluster settings.](https://docs.pushwoosh.com/push-notifications-geo-based-push-10.webp) 2. Enter the **Cluster name** and **Cluster cooldown**. ![Create cluster form in Geo campaigns with cluster name and cooldown settings before saving.](https://docs.pushwoosh.com/push-notifications-geo-based-push-8.webp) 3. Click **Create**. Once created, the cluster appears on the **Geo campaigns** page. To assign geozones to it: * Select **Use existing cluster** in the **Cooldown** field when [creating or editing a geozone](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#choose-a-cluster-and-cooldown). * Use **Move to cluster** on the [Geo campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/#manage-a-geozone) page. You can also create a cluster while you save a geozone: 1. In **Cooldown**, choose **New cluster**. 2. Enter the name and cooldown. 3. Click **Save**. --- # Create a geozone **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/ Configure entry actions, the push message, trigger shape, cooldown, and the campaign schedule when creating a Pushwoosh geozone. --- Create a geozone to define where your campaign triggers and what happens when a user enters that area: a push message, a Customer Journey event, or both. Go to **Campaigns → Geo campaigns**. Click **Create geozone** to start. ![Geo campaigns Get Started screen with map view and Create geozone action in the Pushwoosh control panel.](https://docs.pushwoosh.com/push-notifications-geo-based-push-1.webp) ## Set the campaign name and aggregated statistics In the **Campaign name** field, enter a label up to 64 characters. Keep it short and recognizable, for example, `Spring sale at downtown shops`. Optionally, select an existing campaign in the **Aggregated statistics** field. Geo campaign pushes will then be counted together with other touchpoints in that campaign’s [aggregated messages statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/). ![Geozone form section with Campaign name and Aggregated statistics fields.](https://docs.pushwoosh.com/push-notifications-geo-based-push-2.webp) ## Choose entry actions In the **Entry actions** section, turn on any combination of these actions. Keep at least one turned on, and give each event-based action a different event name when more than one is on. Existing geozones If you created a geozone before this feature launched, it keeps **Send push** on and every event-based action off. Open and save the geozone to change that combination. A geozone configured through the API with an action’s duration set to 0 seconds also shows that action as off in the form. Use this comparison to decide which actions to turn on: | Action | Choose it when | Example campaign | Pairs well with | | ----------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | [**Send push**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#send-push) | You want people to see a message the moment they arrive, no journey needed | ”20% off today” push the moment a shopper walks into the store | **Fire dwell event**, so you can also flag people who stuck around, for a later follow-up message | | [**Fire event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-event) | You want to kick off a longer automated flow (several messages over time), not just one push | `PW_GeozoneEntered` starts a Journey: welcome message → wait 1 day → loyalty-card reminder | **Fire exit event**, so the Journey only ends once you’re sure the person actually left | | [**Fire exit event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-exit-event) | You want to know for sure the person left. That confirmation can arrive late, which is good for a follow-up sent well after the visit | `PW_GeozoneExited` triggers a “How was your visit?” survey once the visit is confirmed over | **Fire event**, so the same Journey that welcomed them on entry also wraps up properly when they leave | | [**Fire dwell event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-dwell-event) | You only care about people who actually spent time there, not people who just walked past | `PW_GeozoneDwell` (Time inside = 15 min) flags “genuinely interested” visitors for a follow-up email | **Assume exit after silence**, so you get both signals in your CRM without ever messaging the person during the visit | | [**Assume exit after silence**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#assume-exit-after-silence) | Your location tracking often loses signal (venues, backgrounded apps), so a confirmed exit isn’t reliable. This fires anyway, on a timer | `PW_GeozoneExitedByTimeout` (Silence = 2 hours) closes out visits at a stadium with weak signal | **Fire dwell event**, so you can close out the visit reliably and still know if they actually engaged while there | ### Send push Sends the push notification you configure in [Compose the push message](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#compose-the-push-message) below. For example, a device entering the zone at 10:00 AM receives the push right away, as long as cooldown allows delivery. Turn it off if you only want an event-based action. The message settings disappear and you don’t need to fill them in. ![Send push toggle enabled in the Entry actions section of the geozone form.](https://docs.pushwoosh.com/push-notifications-geo-based-push-entry-send-push.webp) ### Fire event Fires a [custom event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) when the device enters the zone. Use it as a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) into a Customer Journey. For example, a device entering the zone at 2:00 PM fires `PW_GeozoneEntered` at 2:00 PM. Entry and event happen at the same moment. 1. Turn on **Fire event**. 2. Set the **Event name**. Leave it empty to use the system default event `PW_GeozoneEntered`, or enter your own name (up to 128 characters). The event carries six attributes: `geozone_code`, `geozone_name`, `lat`, `lng`, `range`, and `distance`. If the geozone is assigned to a cluster, it also carries `cluster_code` and `cluster_name`. ![Fire event toggle enabled with the Event name field and event attributes shown below it.](https://docs.pushwoosh.com/push-notifications-geo-based-push-entry-fire-event.webp) Note The event appears in the Customer Journey entry-element picker as soon as you save the geozone, before any device has entered the zone. ### Fire exit event This event doesn’t fire the moment someone leaves the zone. The device detects the exit first. It then reports the exit the next time it contacts Pushwoosh: a periodic background check-in that sends the device’s location, not a live, constant connection. How soon that contact happens depends on the device’s OS and background settings, not on Pushwoosh, so the delay between the actual exit and the event can range from a few seconds to several hours. For example, a device that leaves at 3:00 PM but doesn’t contact Pushwoosh again until 3:45 PM fires the event at 3:45 PM, not 3:00 PM. Cooldown gates entry actions, not this one. If a device enters, exits, and re-enters the same geozone within one cooldown window, the repeat entry is suppressed, but each real exit still fires its own event, so you can end up with more exit events than gated entries for the same string of visits. 1. Turn on **Fire exit event**. 2. Set the **Exit event name**. Leave it empty to use the system default event `PW_GeozoneExited`, or enter your own name. ![Fire exit event toggle enabled with the Exit event name field and event attributes shown below it.](https://docs.pushwoosh.com/push-notifications-geo-based-push-entry-fire-exit-event.webp) ### Fire dwell event Fires a custom event once the device has stayed inside the zone for the duration you set in **Time inside**, counted from entry. How the dwell timer works Unlike **Fire exit event**, this action does not wait for the device to contact Pushwoosh. Pushwoosh starts a timer when the device enters the zone and fires the event when **Time inside** elapses, even if the device sends no further location updates in between. * **Fires once per stay:** a new timer starts only after the device leaves and enters again. * **No contact required:** a device that goes silent right after entering still triggers the event on schedule. For example, with **Time inside** set to 10 minutes, a device that enters at 1:00 PM fires the dwell event at 1:10 PM, even if it has no contact with Pushwoosh in between. 1. Turn on **Fire dwell event**. 2. Set **Time inside** to a value from 1 to 1440 minutes. 3. Set the **Dwell event name**. Leave it empty to use the system default event `PW_GeozoneDwell`, or enter your own name. ![Fire dwell event toggle enabled with the Time inside and Dwell event name fields shown below it.](https://docs.pushwoosh.com/push-notifications-geo-based-push-entry-fire-dwell-event.webp) ### Assume exit after silence Fires a custom event once a device, last confirmed inside the zone, goes quiet (stops sending location updates to Pushwoosh) for the duration set in **Silence before assuming**. Confirmed exit vs assumed exit Unlike **Fire exit event**, this action does not wait for the device to report it left the zone. It fires when the device stays silent for the full **Silence before assuming** duration, even if no location update ever shows it outside. These two actions answer different questions: * **Fire exit event:** the device sent a location update from outside the zone. For a circle zone, the location has to be more than 500 m beyond its edge, so GPS jitter at the border doesn’t count as an exit. * **Assume exit after silence:** Pushwoosh treats the visit as over because the device stopped sending updates. Build a separate Customer Journey trigger for each. If you combine them into one trigger, it fires on either signal and you cannot tell a real exit from a device that simply stopped reporting. For example, with **Silence before assuming** set to 30 minutes, a device last seen inside the zone at 4:00 PM fires the assumed-exit event at 4:30 PM if it stays silent that whole time. 1. Turn on **Assume exit after silence**. 2. Set **Silence before assuming** to a value from 5 to 1440 minutes. 3. Set the **Assumed exit event name**. Leave it empty to use the system default event `PW_GeozoneExitedByTimeout`, or enter your own name. ![Assume exit after silence toggle enabled with the Silence before assuming and Assumed exit event name fields shown below it.](https://docs.pushwoosh.com/push-notifications-geo-based-push-entry-assume-exit.webp) Note Exit, dwell, and assumed-exit events carry one extra attribute beyond the six listed above: `dwell_seconds`, the time the device spent inside the zone from entry to the event. * **Fire exit event** also carries `distance`, since it comes from an actual location update. * **Fire dwell event** and **Assume exit after silence** don’t carry `distance`. Both fire from a server timer, not a location update. ## Compose the push message With **Send push** turned on, choose how to compose the notification: * **Write a message**: enter the push text directly in the form (up to 1024 characters). Use this option for a quick one-time message in a single geozone. * **Use an existing preset**: pick a saved [preset](https://docs.pushwoosh.com/product/content/push-presets/) for full templating with title, deep link, custom sound, and platform-specific payloads. To change the order of presets, click the sort button next to the field. The default is **Name A→Z**, and the order you pick also applies to other push preset dropdowns, such as one-time pushes and the Push element in journeys. ![Push message section of the geo campaign form with Use an existing preset selected, the 30% Sale on Textured Coat preset in the Preset field, its preview, and an arrow pointing to the Name A→Z sort button.](https://docs.pushwoosh.com/push-notifications-geo-based-push-3.webp) Configure optional message settings: * **Play default sound on iOS**: uses the system push sound on iOS devices. * **Save to inbox**: keeps a copy of the push in the in-app inbox after delivery, so the user can open it later. When enabled, set **Inbox expire** (1–30 days) and optionally an **Inbox icon URL**. Full Save to Inbox options: [Save a push to Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/#set-inbox-options). ## Set the trigger location Choose a shape for the trigger location: * **Circle**: search for an address, paste raw coordinates into the search bar, or click anywhere on the map to drop a marker. Drag the handle on the circle to set the **Range**. Range can be set between 50 m and 100 km. Default is 200 m. ![Trigger location set to Circle mode with an address search result and a range circle drawn around a New York location.](https://docs.pushwoosh.com/push-notifications-geo-based-push-5.webp) * **Polygon**: click the map to drop corners, one at a time, tracing the outline of the area, for example a mall floor plan or a venue perimeter. Drag a corner to move it, right-click a corner to delete it. A polygon needs 3 to 100 distinct corners. You don’t need to close the shape by clicking the first corner again. ![Trigger location set to Polygon mode with a five-corner shape drawn around a city block, corner count and the derived monitoring circle shown below the map.](https://docs.pushwoosh.com/push-notifications-geo-based-push-polygon-1.webp) Devices monitor a circle, even for a polygon shape A phone can only monitor a circle, not a polygon shape. When you draw a polygon, Pushwoosh calculates a circle that fully contains it, centered on the shape with a radius reaching its farthest corner (minimum 50 m). The device wakes up and reports its location as soon as it enters that circle. The push itself doesn’t fire yet at that point. It only fires once the server confirms the device is actually inside the polygon you drew. That’s why a push can wake the device earlier than it reaches your shape, but it’s still only delivered once the user has actually entered the area. You can switch the shape when editing a saved geozone: * To convert a polygon geozone to a circle, select **Circle** and set a center and range as usual. * To convert a circle geozone to a polygon, select **Polygon** and draw new corners. * Clearing all corners of a polygon (**Clear shape**) also reverts it to a circle. ## Choose a cluster and cooldown A cooldown is a silent period after a push is sent. For a standalone geozone, the same device won’t receive another notification from it until the cooldown expires, even if it exits and re-enters. If the geozone belongs to a cluster, the cooldown applies to the whole cluster instead: the device stays silent for every geozone in that cluster, not just the one that sent the push. In the **Cooldown** field, choose one of three modes: | Mode | How cooldown is set | Behavior | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------- | | **Standalone** | Set days, hours, and minutes directly for this geozone. Minimum is 1 minute. Maximum is 41 days. | Cooldown applies to this geozone only. | | **Use existing cluster** | Apply the cooldown from an existing [cluster](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/). | The cluster cooldown overrides this geozone’s own cooldown. | | **New cluster** | Create a new cluster and set its cooldown. See [Create a cluster](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/). | The cluster cooldown overrides this geozone’s own cooldown. | ![Cooldown mode dropdown showing Standalone, Use existing cluster, and New cluster options in Geo campaigns.](https://docs.pushwoosh.com/push-notifications-geo-based-push-6.webp) ## Schedule when the campaign can fire The **Campaign schedule** section limits when entry actions can fire. A push or entry event runs only if the geozone is active at that moment and cooldown allows delivery. ### Limit the campaign to a date range These fields are optional. Set them when you want the whole campaign to run within a specific date range, for example a holiday promo from September 10 through October 23, or a limited-time store opening. * **Runs from:** the first day the geozone can fire. * **Runs until:** the last day the geozone can fire. Note * **Both dates are inclusive.** The geozone keeps firing through the end of the **Runs until** day. * **Entry actions only.** The dates limit **Send push** and **Fire event**. **Fire exit event**, **Fire dwell event**, and **Assume exit after silence** keep firing outside the date range, as described in [Manage geozones and clusters](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/). * **Weekly schedule.** The date range works together with the daily mode below, not instead of it. * **Time zone.** **Runs from**, **Runs until**, and weekly hours all use the schedule’s time zone, set from your browser’s time zone the first time a schedule is saved for the geozone. Later edits keep that time zone, even if you save from another region, so set up the schedule from the time zone you want the campaign to follow. A brand-new **Always active** geozone has no schedule time zone yet, so its dates are evaluated in UTC until you edit and save it at least once. ![Campaign schedule Runs from and Runs until fields set to September 10, 2026 and October 23, 2026.](https://docs.pushwoosh.com/push-notifications-geo-based-push-schedule-date-range.webp) ### Choose how the geozone runs Select **Always active** to run around the clock, or **On selected days and hours** to restrict firing to specific time windows per day of the week. #### Always active The campaign fires 24/7. No further schedule configuration is needed. ![Campaign schedule section with Always active mode selected and Runs from and Runs until set to Any date.](https://docs.pushwoosh.com/push-notifications-geo-based-push-schedule-always-active.webp) #### On selected days and hours The campaign only fires inside the windows you set here: 1. Turn on each weekday you want the campaign to fire on. 2. Set a start and end time for that day. ![Campaign schedule set to On selected days and hours with Any date range and Monday through Friday 09:00 to 18:00.](https://docs.pushwoosh.com/push-notifications-geo-based-push-7.webp) Note The control panel supports a single time window per day. If a geozone was configured via API with multiple windows per day, editing the schedule in the form is unavailable. Click **Save** to keep the existing schedule unchanged. To modify it, use the API. Click **Save** to finish, regardless of which mode you chose. The geozone is enabled immediately. If you set a schedule, pushes fire only during the configured windows. ### Which actions follow the schedule * **Send push**, **Fire event**, and other actions that fire when a device enters the zone run only during an active schedule window and when cooldown allows delivery. * **Fire exit event**, **Fire dwell event**, and **Assume exit after silence** follow the visit, not the schedule. If a device entered while the schedule was active, these actions can still fire after the daily window closes. --- # Import geozones from a CSV file **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/import-csv/ Bulk-create Pushwoosh geozones from a spreadsheet, map CSV columns to geozone fields, and validate rows before importing. --- When you have hundreds of locations, such as every store in a chain or every pickup point, placing them on the map one at a time is not realistic. Import them from a spreadsheet instead. ## Prepare the file The file needs a header row and one row per location, and must not exceed 5 MB. That is room for tens of thousands of locations, well past what a single geozone campaign is meant to cover. Three columns are required. In the file you can use short headers such as `range` or `cooldown`. On the **Import geozones** screen the mapping dropdown uses the labels below (for example `range` → **Range, m**). | Column in file | Maps to | Required | Notes | | -------------- | ---------------- | :------: | --------------------------------------------------------------------------------------------------- | | Name | **Name** | Yes | Up to 64 characters. | | Latitude | **Latitude** | Yes | Between -90 and 90. | | Longitude | **Longitude** | Yes | Between -180 and 180. | | Range | **Range, m** | No | Meters, between 50 and 100,000. Falls back to **Default range, m**. | | Cooldown | **Cooldown, s** | No | Seconds, between 60 and 3,596,400 (41 days). Falls back to **Default cooldown, s**. | | Message | **Message** | No | Push text, up to 1024 characters. | | Cluster code | **Cluster code** | No | An existing [cluster](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/) code. | | Preset code | **Preset code** | No | An existing [preset](https://docs.pushwoosh.com/product/content/push-presets/) code. | Every geozone needs something to send, so each row must end up with either a message or a preset. It can come from the row’s own column or from **Message for every zone** / **Preset for every zone** on the import screen. A preset wins when a row has both. ### Example CSV Here is a `geozones.csv` with two locations. The second row leaves `range` empty so **Default range, m** applies: | name | latitude | longitude | range | message | | :------------- | :------- | :-------- | :---- | :-------------------- | | Store Downtown | 40.7128 | -74.0060 | 200 | Welcome to our store! | | Pickup Point | 51.5074 | -0.1278 | | Your order is ready | Representation of `geozones.csv` in plain text format: ```txt name,latitude,longitude,range,message Store Downtown,40.7128,-74.0060,200,Welcome to our store! Pickup Point,51.5074,-0.1278,,Your order is ready ``` Exports from Excel work as they are The separator is detected from the file: comma, semicolon or tab. A comma is also accepted as the decimal separator in coordinates, so `55,7558` in a semicolon-separated export is read correctly. Quoted fields may contain the separator and line breaks. Save the file as UTF-8. Excel offers this as **CSV UTF-8** in the **Save as** dialog. A file saved in a regional encoding instead shows non-Latin names as garbled characters. A byte order mark is fine. It is stripped on reading. ## How to import Follow these steps to upload the file and create the geozones. 1. On the **Geo campaigns** page, click **Import CSV**. The **Import geozones** screen opens. ![Geo campaigns page header with the Import CSV button next to How to use.](https://docs.pushwoosh.com/push-notifications-geo-based-push-import-csv-button.webp) 2. Select your CSV file. 3. Map columns and set defaults on the **Import geozones** screen. Map each CSV column to a geozone field. Under each column header, open the dropdown and choose the field that column should fill: * If Pushwoosh matched the header wrong, pick the correct field in the dropdown. * Set a column to **Do not import** to ignore it. * Map **Name**, **Latitude**, and **Longitude** at minimum. * Map **Range, m**, **Cooldown, s**, **Message**, **Cluster code**, or **Preset code** when those columns are in the file. Note Pushwoosh matches common header spellings automatically, such as `name`, `store`, `title`, `lat`, `latitude`, `lng`, `lon`, `longitude`, `radius`, `range`, `cooldown`, `message`, `cluster`, and `preset`. Set defaults for values the file does not supply: * **Default range, m** and **Default cooldown, s.** Used when the file has no range or cooldown column, or the cell is empty (as with “Pickup Point” in the example). * **Cluster for every zone.** Pick a cluster, or leave **No cluster**. * **Preset for every zone.** Optional push preset for every row without a preset of its own. A preset always wins over message text, so rows that only have their own message also get this preset. Use the sort button next to the field to change the order of presets. * **Message for every zone.** Used when the file has no message column. Rows that already have a message keep their own text. ![Import geozones screen with columns mapped to Name, Latitude, Longitude, Range, m, and Message, plus Default range, m, Default cooldown, s, Cluster for every zone, Preset for every zone, and Message for every zone. Status shows 2 rows ready, 0 skipped.](https://docs.pushwoosh.com/push-notifications-geo-based-push-import-csv-mapping.webp) 4. Check the ready and skipped counts (for example **2 rows ready, 0 skipped**). Fix mapping or the spreadsheet if needed, then click **Check without importing**. Pushwoosh validates the whole file without creating geozones. [Rejected rows](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/import-csv/#why-a-row-gets-skipped) come back with the line number and the reason, so you can fix the spreadsheet and try again with nothing left half-written. 5. When the check looks right, click **Import**. Geozones are created in batches. The result screen reports how many were created and which rows failed. ![Import geozones result screen showing how many geozones were created and which rows failed.](https://docs.pushwoosh.com/push-notifications-geo-based-push-import-csv-result.webp) Imported geozones are active Imported geozones are created active and start triggering pushes straight away. [Set a schedule](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#schedule-when-the-campaign-can-fire) for each geozone after the import, or [pause them](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/#manage-a-geozone) on the Geo campaigns list, if they should not fire yet. Importing the same file twice creates a second copy of every geozone: names are not unique. To correct a mistake, delete the geozones you imported and import the corrected file. ### Why a row gets skipped During check or import, rows that cannot be read are listed with their line number and skipped. The rest are still imported. A row is skipped when: * the name is empty * the latitude or the longitude is not a number, or falls outside the range given in the table above * the range is not a number, or falls outside 50 to 100,000 m * the cooldown is not a number, or falls outside 60 to 3,596,400 s * the row ends up with neither a message nor a preset To create geozones from your own system rather than a file, use the [addGeoZone API method](https://docs.pushwoosh.com/developer/api-reference/geozones/#addgeozone). --- # Manage geozones and clusters **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/manage/ View geozone status, then pause, edit, move, or delete geozones and clusters right from the Geo campaigns list in Pushwoosh. --- The **Geo campaigns** page shows all your geozones plotted on a map and listed in a table, grouped by cluster. Standalone zones appear under a **Without cluster** section. ![Geo campaigns list with one cluster and standalone geozones showing Active, Expired, Scheduled, and Paused status.](https://docs.pushwoosh.com/push-notifications-geo-based-push-9.webp) The **Status** column can show: * **Active.** The geozone can fire. Cooldown and the campaign schedule still apply. * **Scheduled.** **Runs from** is in the future. The geozone doesn’t run entry actions (**Send push**, **Fire event**) yet. * **Expired.** **Runs until** has passed. The geozone no longer runs entry actions. * **Paused.** You paused the geozone, or its cluster if **via cluster** appears next to the status. Click **Resume** on the geozone or the cluster, whichever you paused, to let it fire again. **Scheduled** and **Expired** geozones stay enabled in the system and resume sending on their own when the date range applies again. You don’t need to pause or re-enable them for the date range alone. Exit, dwell, and assumed-exit events don’t depend on the dates and keep firing in these geozones. ## Manage a geozone For each geozone you can: * **Pause** or **Resume** the geozone. A paused geozone fires nothing: no entry actions, and no exit, dwell, or assumed-exit events. * **Edit** the geozone in the form. * **Move to cluster.** Assign to a different cluster, or remove from its current cluster. To create a cluster first, see [Create a cluster](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/clusters/). * **Delete** the geozone permanently. ## Manage a cluster For each cluster you can: * **Pause** or **Resume** the cluster. Pausing a cluster stops entry actions (**Send push**, **Fire event**) for every geozone inside it. Exit, dwell, and assumed-exit events keep firing. * **Edit** the cluster name or cooldown. * **Delete** the cluster. Only clusters without attached geozones can be deleted. --- # Import push subscribers **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/import-push-subscribers/ Import existing push tokens from OneSignal or Amazon Pinpoint into Pushwoosh. Users keep their subscriptions without reinstalling. --- If you’re migrating from another push provider, you can import your existing push subscriber base into Pushwoosh from a CSV file. Tokens keep working as long as the app still uses the same APNs key, Firebase project, or HMS credentials. Users don’t need to reinstall the app or grant permission again. This import registers devices with push tokens. It doesn’t create a segment. Imported devices get a **CSV import** tag that you can use to build one afterward. To import a list of already-registered User IDs or Hardware IDs into a segment instead, see [Import a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/). ## Supported platforms Pushwoosh imports push tokens only for the platforms listed below. The `device_type` column accepts two different formats, depending on where your file came from: * A number, if your export uses OneSignal’s own platform codes. * A word, if it’s an Amazon Pinpoint export or a file you built by hand. Don’t mix the two formats in one file, because the same number means a different platform in each system. Pasting in a number from a non-OneSignal source can silently import devices under the wrong platform. | Platform | OneSignal `device_type` number | Accepted platform names | | :----------- | :----------------------------- | :----------------------------------------- | | iOS | `0` | `ios`, `apns`, `apns_sandbox`, `apns_voip` | | Android | `1` | `android`, `gcm`, `fcm` | | Amazon | `2` | `amazon`, `adm`, `fireos` | | Windows | `6` | `windows`, `wns` | | macOS | `9` | `macos`, `osx` | | Huawei (HMS) | `13` | `huawei`, `hms` | Rows with any other value are skipped (see [Why a row gets skipped](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/import-push-subscribers/#why-a-row-gets-skipped)), including: * OneSignal’s own web push, email, and SMS device types. * Platforms Pushwoosh doesn’t support, like the discontinued Windows Phone or Baidu push. Web push subscriptions can’t be transferred from any provider. Those users need to re-subscribe through the Pushwoosh SDK on your website. ## Prepare a CSV file Your CSV must be under 100 MB. Name the columns exactly as shown below. Letter case does not matter. You cannot map columns in the UI, so rename any headers that don’t match: | Header | Maps to | | :---------------------------------- | :------------------------------------------------------------- | | `identifier` | Push token (required) | | `device_type` | Platform (required) | | `external_id` or `external_user_id` | User ID | | `language` | Device language | | `tags` | A JSON object of custom tag values | | `invalid_identifier` | OneSignal’s “unsubscribed” flag. Rows flagged here are skipped | A timezone column, if present, isn’t imported, because its format differs between export versions. ### Example CSV Here’s a `subscribers.csv` with two devices, one using a platform name and one using a OneSignal numeric code: | identifier | device_type | external_id | language | tags | | :------------------- | :----------- | :----------- | :------- | :---------------- | | `ios-token-0001` | `ios` | `user-1001` | `en` | `{"plan":"pro"}` | | `android-token-0002` | `1` | `user-1002` | `en` | `{"plan":"free"}` | Representation of `subscribers.csv` in plain text format: ```txt identifier,device_type,external_id,language,tags ios-token-0001,ios,user-1001,en,"{""plan"":""pro""}" android-token-0002,1,user-1002,en,"{""plan"":""free""}" ``` Note The `tags` value is a JSON object with quote marks inside. If you build the file in Excel or Google Sheets, they quote the cell for you. If you edit it in a plain text editor, wrap the whole value in quotes and write every quote mark inside it as two quotes (`"` becomes `""`), as in the example above. ## How to import 1. Go to **Audience** → **Import CSV** and select the **Import push subscribers** card. ![Import CSV screen with Update Tag Values, Import push subscribers, and Import events cards.](https://docs.pushwoosh.com/push-notifications-import-push-subscribers-1.webp) 2. Select your CSV file. Caution Pushwoosh checks the whole file when you select it. A row with an unclosed quote gets the file rejected right away. A mismatched column count is tolerated here, unlike the other CSV imports. A toast names the row and the reason, for example “Row 3: a quote mark isn’t closed properly.” Nothing is imported in this case. Fix that row and select the file again. ![Toast titled The file cannot be imported, showing Row 3: a quote mark isn't closed properly. Nothing was imported.](https://docs.pushwoosh.com/push-notifications-import-push-subscribers-malformed-toast.webp) 3. Review the summary: * Total rows in the file, and how many are importable, broken down by platform. * Skipped rows, broken down by reason, with a link to download the skipped rows as a CSV (original columns plus a `skip_reason` column). * Custom tags detected in the `tags` column, with the type Pushwoosh inferred for each (`string`, `integer`, `boolean`, or `date`). If a tag name already exists in your account with a different type, it’s flagged as a conflict. You can change the type or skip that tag for this import. ![Map step summary showing rows imported by platform and skipped rows broken down by reason.](https://docs.pushwoosh.com/push-notifications-import-push-subscribers-2.webp) 4. Click **Start import**. When the import finishes, check the counts: * **Devices created:** new devices. * **Devices updated:** devices that already existed. * **Skipped rows:** rows left out at the Map step (step 3), broken down by reason, with a download link. ![Import finished screen showing Devices created, Devices updated, and skipped rows broken down by reason with a download link.](https://docs.pushwoosh.com/push-notifications-import-push-subscribers-3.webp) Note Older imports show a single **Devices imported** count instead of **Devices created** and **Devices updated**. ### Why a row gets skipped These are the skip reasons shown in the Map summary and in the skipped-rows CSV. | Reason | Cause | | :------------------- | :---------------------------------------------------------------------------------------------------------- | | Web subscriptions | Bound to another provider’s keys. Can’t be transferred | | Invalid tokens | Already rejected by APNs / FCM (OneSignal’s `invalid_identifier` flag) | | Email addresses | Use [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) instead | | Phone numbers | Use [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) instead | | Duplicate tokens | The same push token appears more than once in the file. The first row wins and later rows are skipped | | Unsupported platform | Platform isn’t one Pushwoosh supports (e.g. Windows Phone, Baidu) | | Unknown platform | The platform value isn’t recognized at all | | Empty push token | No value in the push token column | ## Re-importing the same file Pushwoosh generates each imported device’s Hardware ID (HWID) from its push token, and the same token always produces the same HWID. So importing the same file again updates those existing devices instead of creating duplicates. It’s safe to re-run an import after fixing a mapping issue, or to periodically re-sync tag values from a new export. ## After the import Imported devices are tagged with **CSV import**, valued with the import date and time. The same tag is used by [Segment import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/). Use it to build a segment from the imported devices, or find them individually in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). --- # Managing your push subscriber base **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/manage-push-subscriber-base/ Ensure your messages reach active users and avoid paying for inactive ones by managing your push subscriber list. --- In Pushwoosh, each subscriber is identified by a push token, a unique identifier that enables message delivery to a specific device. This post covers how push tokens work and how to keep only valid tokens in your subscriber base. ## Understanding push tokens and how they work A push token is a unique, anonymous identifier assigned to a specific app instance on a device by its push service provider: * Apple Push Notification Service (APNs) for iOS devices * Firebase Cloud Messaging (FCM) for Android devices This token ensures that push notifications are delivered to the correct app on the user’s device. In Pushwoosh, the push token lifecycle includes these key stages: 1. When the app is launched for the first time, it prompts the device’s operating system (OS) for permission to receive push notifications. 2. The application’s push service (e.g., APNs for iOS, FCM for Android) generates a unique push token for that app instance. By default, the Pushwoosh SDK collects and stores this token even if the user has not granted notification display permissions. Push messages won’t be shown to these users, but the token can still be used to send messages to their application inbox, another messaging channel in Pushwoosh. However, you can configure the SDK to collect tokens only from users who have granted permission. 3. The app uses the Pushwoosh SDK to send this token to the Pushwoosh database along with device data (including a hardware ID or HWID) to Pushwoosh via the [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) API endpoint. 4. Pushwoosh securely stores the token for future messaging. 5. When a notification is triggered, Pushwoosh uses the stored token to route the message through the appropriate delivery gateway. 6. The chosen gateway delivers the message to the device, and the OS displays it based on system and app notification settings. 7. If the push token becomes invalid (e.g., due to app uninstallation), Pushwoosh detects the delivery failure after another notification send-out, when the gateway returns the list of send-out errors, including expired/invalid/etc., tokens. Then Pushwoosh marks the token as inactive and deletes it from the database. If the app is reinstalled on the same device, a new token will be issued. ### Common reasons push tokens become invalid While Apple Push Notification Service (APNs) and Firebase Cloud Messaging (FCM) do not provide full transparency into token lifecycle policies or allow proactive token validation, there are known scenarios in which push tokens can become invalid or expire. | Cause | APNs (iOS) | FCM (Android) | | :------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **App uninstall** | Token becomes invalid after app uninstall. APNs eventually returns a `410 Unregistered` error. Apple does not specify when the invalidation occurs; it can take anywhere from a few minutes to several days. [Learn more](https://developer.apple.com/forums/thread/670868) | Token is immediately invalidated upon uninstall. Subsequent sends return an `Unregistered` error. [Learn more](https://help.pushwoosh.com/hc/en-us/articles/19861855131805-Sudden-increasing-of-unregistered-errors-on-the-Android-platform) | | **Token refresh** | APNs issues a new token after OS upgrades, device backup/restore, app reinstall, or OS updates. [Learn more](https://developer.apple.com/documentation/usernotifications/registering-your-app-with-apns#:~:text=Never%20cache%20device%20tokens%20in%20local%20storage.%20APNs%20issues%20a%20new%20token%20when%20the%20user%20restores%20a%20device%20from%20a%20backup%2C%20when%20the%20user%20installs%20your%20app%20on%20a%20new%20device%2C%20and%20when%20the%20user%20reinstalls%20the%20operating%20system.%20You%20get%20an%20up%2Dto%2Ddate%20token%20each%20time%20you%20ask%20the%20system%20to%20provide%20the%20token.) | Same behavior as APNs. Also triggered by app data clearance or device restoration. [Learn more](https://firebase.google.com/docs/cloud-messaging/manage-tokens?utm_source=chatgpt.com#retrieve-and-store-registration-tokens) | | **Token inactivity** | No documented expiration due to inactivity. [Learn more](https://developer.apple.com/forums/thread/671310) | Tokens inactive for over 270 days are considered stale/expired. FCM marks them invalid. [Learn more](https://firebase.google.com/docs/cloud-messaging/manage-tokens#stale-and-expired-tokens) | Important Pushwoosh determines token validity only when a push notification fails to deliver. It does not proactively check token status. ## How to keep your subscriber list clean by removing invalid push tokens To keep your push subscriber list clean, regularly check delivery stats, remove invalid tokens, and ensure your app and backend always have the latest token data. Pushwoosh offers several ways to help you do this: ### Send push notifications to your entire subscriber base regularly Send push messages to your entire subscriber base regularly. This helps Pushwoosh identify invalid tokens. When a message fails to deliver, typically due to an invalid token, the push gateway returns an error. The SDK captures this response and removes the invalid token automatically, ensuring it is excluded from future sends. > **Important:** Token validity is only checked when a push is sent. Pushwoosh does not perform background checks on token validity. ### Unregister devices using the API Developer assistance needed Unregistering devices via the API requires assistance from your development team. Please share this guide with them to implement the process. You can use the [`/bulkUnregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkunregisterdevice) Device API method to remove push tokens from your subscriber base, for example, when users opt out or are no longer active. To do it: 1. Build a segment of users matching your criteria (e.g., opted‑out, free-tier users, or those inactive for X days). 2. Export the segment using the Control Panel or the [`exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment) API method. 3. Call [`/bulkUnregisterDevice`](https://docs.pushwoosh.com/developer/api-reference/audience-api/#bulkunregisterdevice) to delete the push tokens for the devices in that segment. ### Automatically remove inactive tokens after 90 days with autovalidation The automatic inactivity cleanup algorithm in Pushwoosh helps keep your subscriber list clean by removing push tokens from users who haven’t been active for 90 days. It works by tracking when users last opened your app using the default **Last Application Open** tag. If a user hasn’t opened the app for 90 days or more, their push token is marked as inactive and removed from your subscriber base. Important This feature is enabled by default for all accounts created on or after January 1, 2024. To check whether it’s active for your account or to request activation, please [contact Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new/). ### Use Uninstall tracking Uninstall Tracking keeps your push subscriber list accurate by detecting when users uninstall your app. This allows Pushwoosh to automatically remove invalid tokens. > **Note:** Uninstall Tracking is supported only on **iOS** and **Android**. It works as follows: 1. Pushwoosh sends a silent push notification once per day to all subscribed devices. These silent notifications are invisible to users, with no alerts, sounds, or badges. 2. Each device has a unique push token provided by a notification gateway. If the app has been uninstalled, the token becomes invalid. When the silent push fails, the gateway returns an error. 3. Pushwoosh detects the error and removes the invalid token from your subscriber list. Important Uninstall Tracking sends silent push notifications to all subscribers daily, which may increase system load. Ensure your infrastructure can support this volume. The feature is enabled by default on supported platforms. To disable it, [contact Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new/) or your Customer Success Manager. #### Limitations of uninstall tracking Notification gateways do not guarantee delivery of silent pushes. These messages may be deprioritized or dropped by the gateway, which can delay or limit the accuracy of uninstall detection. [Learn more](https://help.pushwoosh.com/hc/en-us/articles/26713265335581-Understanding-Silent-Push-Notification-Behavior-and-Limits-on-iOS) #### View uninstalls data To monitor uninstall activity, navigate to the **Statistics > Dashboards > Application dashboard** in your Pushwoosh Control Panel. The **App Uninstalls** graph provides a timeline view of uninstall trends for your application. ## Can you restore users after push token deletion? Deleting a push token using any supported method is easily reversible. When a user re‑opens your app: 1. The Pushwoosh SDK automatically requests a new push token. 2. It then re‑registers the device (via [`registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice)). 3. The user becomes an active subscriber again and will receive push notifications going forward. --- # How to send push notifications **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/ Send one-time, recurring, scheduled, or targeted push notifications. Use the campaign form or Customer Journey to reach your audience. --- Pushwoosh offers several ways to send push notifications, depending on when and how you want to reach your audience. #### One-time push Send a single notification to a segment or all users. Deliver immediately or schedule for a specific time. [One-time push ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/) [One-time push via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/) #### Recurring push Set up one message and have it sent automatically on a schedule: daily, weekly, or on specific dates you choose. [Recurring push ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes/) #### Scheduled push Schedule a one-time push for a specific date and time, or set up a push campaign with Customer Journey Builder. [Scheduled push ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/scheduled-push/) #### Targeted push Send push messages to specific audience segments using Customer Journey Builder. [Targeted push ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/targeted-push/) #### Push to all user devices Send a notification to all devices associated with a User ID. [Send push to all user devices ](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/push-by-user-id/) #### Save a push to a user’s inbox When you send a push, you can also keep a copy in the app’s inbox so users can revisit it later. This is set on the send (one-time or journey), not in the push preset. [Save a push to Inbox ](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/) --- # Recurring push **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes/ Set up recurring push notifications that send automatically on a schedule (daily, weekly, or custom dates). Same flow for mobile and web push campaigns. --- Recurring push sends the same push notification repeatedly at a chosen interval (e.g. daily, weekly) or on selected dates. The flow is the same for mobile and web push. **Prerequisites** * Recurring schedule is only available with [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). * Build a journey with [**Audience-based Entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) and [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), then end the branch with an [**Exit**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element or by leaving it without an outgoing connection. See [One-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#how-to-send-a-one-time-push-notification-using-customer-journey-builder) for the basic flow, then configure the entry schedule below. ## Configure recurring entry Double-click the [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#recurring-entry) element. Set **Entry schedule** to **Recurring entry**, then choose: * **On specific dates:** users enter on the dates you pick (e.g. webinar or event dates). * **Periodically:** users enter at a fixed interval (e.g. every 1 day, every 1 week). Set the time and timezone as for a one-time scheduled send. [Learn more about recurring entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#recurring-entry). ### On specific dates Schedule the journey so users enter on multiple specific dates. 1. Select **On specific dates** and pick the dates in the calendar. Click **+ Add Date** to add more dates. 2. Set the time and timezone. ![Audience-based entry: Recurring entry on specific dates](https://docs.pushwoosh.com/journey-elements-audience-based-entry-5.webp) **Timezone options:** * **Subscriber’s device timezone (default):** entry uses each subscriber’s device timezone. If timezone is missing, a fallback is used. Click **Change fallback** to change it. * **Specific timezone:** pick a timezone from the dropdown for a fixed entry time. Note Subscribers whose scheduled time for a specific date has already passed will be included on the next scheduled date. ### Periodically Set up a journey that runs at a fixed interval (e.g. every Monday at 9:00 AM). 1. Select **Periodically**. 2. In **ENTRY EVERY**, set the interval (e.g. every 1 day, every 2 weeks). 3. Pick the days (of the week or month, depending on the interval). 4. Set the time and timezone the same way as for [On specific dates](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes/#on-specific-dates). ![Audience-based entry: Recurring entry periodically with interval, selected days, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-6.webp) Note Subscribers whose scheduled time has already passed will be included on the next scheduled day. ## Next steps 1. Configure the [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element (message content). 2. Configure any other journey elements you need. 3. End the branch with an [**Exit**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element, or leave it without an outgoing connection. [Learn more about Journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements). ## Example: Weekly reminder for a fitness app Send the same motivational push every Monday at 9:00 AM in each user’s local timezone. 1. **Create a basic push flow.** Add [**Audience-based Entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) to the canvas and select an **Audience Source**. 2. **Set recurring entry.** In the element, set **Entry schedule** to **Recurring entry** → **Periodically**, **ENTRY EVERY** to **every 1 week**, day **Monday**. 3. **Set time and timezone.** Set time to **9:00 AM** and **Subscriber’s device timezone**. 4. **Add Push and end the branch.** Add [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) (create the message), then end the branch with an [**Exit**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element or by leaving it without an outgoing connection. Review and click **Launch campaign**. Example message * **Title:** Keep Moving! 💪 * **Message:** Hey [First Name], log your workout and stay on track with your fitness goals. Let’s make this week count! 🏋️‍♂️ ![Push notification content form showing Title and Message fields with example fitness app motivational content](https://docs.pushwoosh.com/shared-32.webp) --- # One-time push **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/ Send a one-time push notification in Pushwoosh. Choose content, audience, schedule, and optional Inbox save or linked in-app message. --- Tip Watch this video to learn how to send a one-time push notification in Pushwoosh, or refer to the guide below for more detailed instructions. [Youtube Video: How to send a one-time push notification in Pushwoosh](https://youtube.com/watch?v=046g_5xgQx8) ## How to send a push notification using the one-time push form To send a one-time push notification in Pushwoosh, go to the **Campaigns** section. Select **One-time messages** and click **Send message** → **One-time push**. ![Campaigns section showing One-time messages option with Send message button and One-time push option](https://docs.pushwoosh.com/shared-51.webp) ### Select or create push message content To start, either create new content or select existing content for your push notification. If you’d like to use a pre-built push preset, choose it from the dropdown menu of available options. ![Content step of the push form with the 20% Discount Voucher preset selected and an arrow pointing to the Name A→Z sort button above the preset dropdown](https://docs.pushwoosh.com/shared-1.webp) To change the order of presets in the dropdown, click the sort button above it. The button shows the current order, which is **Name A→Z** until you pick another one. Pushwoosh remembers the order you pick and uses it in every push preset dropdown you open: one-time and scheduled pushes, the [Push element in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), and [geozones](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/). To create a new message, click **Create new** and [follow this guide](https://docs.pushwoosh.com/product/content/push-presets) to set up a new push preset. Note The push preset you create here won’t be saved in the Push presets list to keep it organized and uncluttered. If you need to save a preset for future use, go to [Push presets](https://docs.pushwoosh.com/product/content/push-presets), create it in advance, and then select it from the dropdown menu. If you need to edit an existing preset, click **Edit Content** to modify the message. Note If [content approval](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) is on for your account, **Create new** isn’t available here, and the dropdown lists unapproved presets but keeps them unselectable. [Submit and approve](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/#submit-content-for-approval) the preset from **Content** → **Push presets** first, then select it from the dropdown. ![One-time push form Content step showing an On approval preset greyed out and unselectable in the dropdown](https://docs.pushwoosh.com/approvals-and-content-versions-6.webp) A preview of the notification will be displayed on the right side, showing how it will appear to users. ### Select message type In the **Content** step, choose **Marketing message** or **Transactional message**: * **Marketing message:** Respects frequency capping. Not sent to the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message:** Sent to all users regardless of subscription status. Delivered to the control group. No message type is preselected for a new push. Until you choose one, the form shows a **Message type not selected** warning, and you can’t move to the next step. ![Content step with no message type selected and the Message type not selected warning](https://docs.pushwoosh.com/push-notifications-one-time-push-33.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ### Define what happens when the push is clicked After selecting or creating the content for your push notification, configure what happens when a user taps the notification using the **Show in-app banner when message is clicked** toggle. Note The **Show in-app banner when message is clicked** option is available for mobile platforms only. By default, the toggle is off, and the [action configured in the push preset](https://docs.pushwoosh.com/product/content/push-presets/#define-the-next-step-for-users-who-click-on-the-notification) will be executed when the user taps the push notification. Use this default behavior when your push preset already includes a well-defined click action. To display an in-app banner instead, enable the **Show in-app banner when message is clicked** toggle, then select the in-app message to display from the **in-app banner** dropdown. This is useful for delivering additional information, promotions, or forms directly within the app. A live preview of the selected banner will appear on the right. The banner list starts with **Oldest first**. To change the order, click the sort button above the dropdown. The order you pick also applies to the other in-app dropdowns. ![IN-APP BANNER field with Welcome Newcomers selected and an arrow on the Oldest first sort button](https://docs.pushwoosh.com/push-notifications-one-time-push-30.webp) ##### Understand language behavior for push and in-app messages Push notifications and in-app messages can support different sets of languages. The language displayed to the user depends on the availability of that language in each content type and the user’s device language. ##### Example You send a push notification using a preset that supports English (default), Spanish, and German.\ The linked in-app banner supports only English and French.\ The user’s device language is set to German. * The **push notification** will be shown in German, since it’s supported on the user’s device. * The **in-app banner** will fall back to English, as German is not available in the banner’s language settings. Tip To ensure a consistent user experience, make sure to align the supported languages in both your push presets and in-app messages. ### Save message to Inbox Toggle **Save message to Inbox** to keep a copy of this push in the app’s inbox. Users still get the push banner. They can open the same message later if they dismiss or miss it. Full setup: [Save a push to Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox/). When the toggle is on, the inbox title and message come from the push preset. ![Save message to Inbox toggle switch enabled with inbox configuration options visible](https://docs.pushwoosh.com/push-notifications-one-time-push-31.webp) ##### Remove from Inbox In **Remove from Inbox**, choose how long the message stays: * **After the specified number of days:** set the number of days in the input field. * **On a specific date:** pick the date the message leaves the inbox. Note The maximum retention period is 30 days. ##### Message icon URL In **MESSAGE ICON URL**, set an image URL for the inbox list, or click the paperclip to pick an image from the [Media storage](https://docs.pushwoosh.com/product/content/media-store/). If you leave it empty, the default icon is used. ![Message icon url field with a paperclip button to open Media storage](https://docs.pushwoosh.com/push-notifications-one-time-push-38.webp) Selecting an image fills the field with its Media storage URL and updates the icon preview. ![Message icon url field populated with a Media storage image URL after selection](https://docs.pushwoosh.com/push-notifications-one-time-push-40.webp) ### Select audience for push notification Next, choose the audience for your push notification. You can either send the notification to all users or target a specific segment based on user behavior or demographics. #### Send to segment Select this option to target a [specific audience segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/). Choose a pre-built segment from the dropdown menu, or create a new segment by clicking **Create Segment**. Then select one of the following actions from the dropdown menu: * **Build segment**: Create a new segment using the Segment builder. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) * **Import segment**: Import a segment from a CSV file. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment). ![Audience selection screen showing Send to segment option with segment dropdown and Create Segment button with dropdown menu showing Build segment and Import segment options](https://docs.pushwoosh.com/shared-60.webp) #### Send to all users Choose this option to send the notification to all your subscribers. This is ideal for general announcements or promotions that apply to your entire user base. Important Ensure the message content is relevant to all recipients. Sending irrelevant or uninteresting notifications may result in user frustration or increased opt-outs. ![Send to all users option selected for push notification audience with warning message](https://docs.pushwoosh.com/shared-22.webp) #### Target platforms Select the platforms on which you want the notification to be delivered. Available platforms include: * iOS * Android * Safari * Chrome * Firefox Choosing target platforms ensures that your message reaches users on the appropriate devices. Only users on the selected platforms will receive the notification. ![Target platforms selection showing iOS, Android, Safari, Chrome, and Firefox checkbox options](https://docs.pushwoosh.com/shared-46.webp) #### Send to last active device Turn on **Send to last active device** to send the push to only one of a user’s devices instead of every device they own. Pushwoosh picks the device where the app was opened most recently among the platforms you selected above, whether that’s a phone, tablet, or other device. Devices without a linked user ID aren’t affected, and neither are Apple/Google Wallet passes: those always go out to every registered device. ![Send to last active device checkbox below Target platforms on the Audience step, with an info tooltip icon](https://docs.pushwoosh.com/push-notifications-one-time-push-41.webp) The toggle is off by default, so existing one-time pushes keep going to all of a user’s devices until you turn it on. Example: Send to last active device A user has a phone and a tablet linked to the same user ID and matches your audience. The user last opened the app on the tablet. With the toggle off, both the phone and the tablet receive the push. With it on, only the tablet receives it. Once you’ve configured your audience and selected the target platforms, click **Next** to proceed with setting up your push notification. ### Schedule push notification Next, choose when to send your push notification. You have the following options: * send it immediately * schedule it for a specific time * let Pushwoosh determine the optimal time for each user based on their behavior. #### Set time to live for push delivery Use **Time to live** in **Sending options** to control how long Pushwoosh retains a push notification for delivery if the recipient is offline at the time of the send attempt. In **Time to live**, choose a preset period for how long Pushwoosh keeps this message available for delivery, from 1 hour to 3 weeks. ![One-time push delivery settings showing the Time to live selector with preset periods for message expiration.](https://docs.pushwoosh.com/push-notifications-one-time-push-35.webp) You can also choose **Custom range** and enter a specific number of hours, days, or weeks. The maximum period is 4 weeks. ![One-time push Time to live settings with Custom range selected for entering a specific expiration period.](https://docs.pushwoosh.com/push-notifications-one-time-push-36.webp) Note The countdown starts when Pushwoosh attempts to deliver the one-time push, not when you create or schedule the message. #### Send immediately Select this option if you want to send the push notification immediately after setting up the campaign. This is useful for urgent or time-sensitive messages. ![Send immediately option selected for push notification scheduling showing immediate delivery option](https://docs.pushwoosh.com/push-notifications-one-time-push-1.webp) #### Selected time Choose this option to schedule your push notification for a specific date and time. 1. Use the calendar to select the exact date for your notification. 2. Set the time in hours and minutes (using the 24-hour format) for when the notification should be sent. 3. Choose the appropriate time zone: * **Subscriber’s device timezone**. The notification will be sent based on the recipient’s local time, ensuring delivery at the specified time in their timezone. * **Custom timezone**. Select a specific timezone to schedule the notification for all recipients, useful for targeting users in a specific region. ![Schedule your push notification for a specific date and time](https://docs.pushwoosh.com/push-notifications-one-time-push-24.webp) Tip You can cancel a scheduled message before it is sent. The message status must be **WAITING**. On the One-time messages list, open the actions menu (⋮) for the message and choose **Delete message**. ![One-time message row with actions menu open showing Message details and Delete message options](https://docs.pushwoosh.com/push-notifications-one-time-push-32.webp) #### Best time (paid subscription required) This option sends each message at the user’s best time based on their behavior and past message performance. Select the start date, and Pushwoosh schedules delivery automatically. You can schedule messages from tomorrow up to one month ahead. If there is not enough data for some users, set **Fallback time** in hours and minutes. In this case, the message is sent on the selected date or one day later at the fallback time in the user’s timezone. ![Best time scheduling for one-time push with date selection, fallback time fields, and requirements information.](https://docs.pushwoosh.com/push-notifications-one-time-push-37.webp) To use the **Best time** feature, make sure that: * [**Default app events**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events) are enabled. * You have previously sent messages at different times to collect enough user data for the system to determine optimal send times. #### Configure message delivery settings ##### Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive push messages, preventing over-messaging and reducing churn. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is set to 3 messages in 9 days, additional messages exceeding this limit will be skipped. ![Frequency capping settings showing Use global frequency capping settings option selected](https://docs.pushwoosh.com/push-notifications-one-time-push-25.webp) * **Send messages without global frequency capping** The user will receive this message even if they’ve exceeded the channel’s message limits. Use this option with caution to avoid over-messaging. ![Frequency capping settings showing Send messages without global frequency capping option selected](https://docs.pushwoosh.com/push-notifications-one-time-push-26.webp) * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message will be skipped, and the user will proceed to the next step. Under **Counting mode**, choose how sent messages are counted: * **Rolling window**: counts the messages sent within the last N×24 hours before each send, where N is the number of days in the limit. For example, with a limit of 3 messages per 2 days, a message sent Monday at 9 AM stops counting on Wednesday at 9 AM. * **Calendar day**: counts the messages sent today and on the previous N-1 calendar days, starting at midnight. With the same limit of 3 messages per 2 days, on Wednesday every message sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. Select a **Capping timezone** to set when midnight falls. If you don’t select one, calendar days are counted in UTC. ![Use custom frequency capping with message limit fields and Counting mode set to Calendar day with a Capping timezone selector](https://docs.pushwoosh.com/push-notifications-one-time-push-27.webp) Important Custom frequency capping replaces global capping for this message, so only the custom limit is checked. The message still counts toward the channel’s global counter, though, so it can use up part of the shared quota for other push messages that rely on global capping. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#use-custom-frequency-capping). ##### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings**\ Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate**\ Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate**\ Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) Once you’ve configured the desired options, click **Next** to proceed to the confirmation step. ### Review and edit your content Before finalizing, review the content, on-click actions, audience, platform, and scheduling options to ensure everything is configured correctly. You’ll also see a preview of your push notification in each selected language. You can adjust any settings as needed. ![Review and edit content screen showing push notification preview and configuration summary in multiple languages](https://docs.pushwoosh.com/shared-15.webp) Optionally, you can choose to link your push notification to an **Aggregated Campaign**, allowing you to associate this message with a larger, ongoing campaign and track its performance alongside related notifications over time. ![Link to Aggregated Campaign option toggle for associating push notification with larger campaign](https://docs.pushwoosh.com/shared-56.webp) 1. Click **Schedule push** or **Send now**. The **Confirm sending** dialog opens. 2. Optionally edit **Campaign name**. It’s prefilled with the push notification’s title, or with its body text if the title is empty. Keep it to 255 characters or fewer. Longer text is cut off without a warning. 3. Click **Schedule push** or **Send now** in the dialog. ![Confirm sending dialog with Campaign name field prefilled from the push notification's body text, since this preset has no title](https://docs.pushwoosh.com/shared-63.webp) The campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the notification title. If you clear the field, Message History shows the title or body text. A campaign name only labels this one send. To group the push with other messages, link it to an Aggregated Campaign (see above). ### Cancel or edit a scheduled message For messages that are scheduled and not yet sent (status **Scheduled** in the list), you can change or cancel them. Open the list of messages in **Campaigns** → **One-time messages**. Open the actions menu (⋮) for the message you want to change or cancel: * **Delete message:** cancels the scheduled delivery and changes the status to **Stopped**. ![One-time message row with status Stopped after Delete message](https://docs.pushwoosh.com/push-notifications-scheduled-push-3.webp) * **Edit message:** opens the one-time push form with the message data loaded. You can change content or audience. * **Rename:** sets or changes the campaign name without opening the edit form. The limit is 255 characters. To remove the name, clear the field and save. The row then shows its default title. While editing * When editing, you can update **content** and **audience** only. The scheduled send time is fixed and cannot be changed. To change the send time, cancel the message (**Delete message**) and create a new one. * On the **Audience** step you can change the segment, create a new one, or change the device list. The audience size is recalculated after you make a change. * If the message enters **Processing** (message is being sent) while you are editing, saving is disabled. Use **Go to Message History** or **Close** to leave the form. * In Review, the button is labeled **Apply changes** instead of **Schedule push** or **Send now**. Click it, then confirm again in the **Confirm sending** dialog to save your changes, including any edit to the campaign name. ![One-time message row with actions menu open showing Edit message, Message details, and Delete message options](https://docs.pushwoosh.com/push-notifications-one-time-push-32.webp) ### Example scenario Imagine you’re a marketing manager for a fitness app, and you want to send a one-time push notification about a **flash sale** on premium memberships. Do it as follows: ##### Create the campaign Go to **Campaigns**, select **One-time messages**, and click **Send message** → **One-time push** to begin setting up your campaign. ##### Create message content Since you want to create a new message for this flash sale, click **Create new** and set up a push preset titled **Flash Sale: 50% Off Premium Membership**. Example text for a push message: *“Limited Time Offer! Get 50% off Premium Membership for the next 24 hours. Don’t miss out on exclusive workouts and features!”* In the push preset, add a [deep link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking) to direct users to the app’s membership page. Once you’re satisfied with the design and content, click **Next** to proceed to the audience selection step. ![Push preset creation form showing Flash Sale push notification content with title and message fields](https://docs.pushwoosh.com/push-notifications-one-time-push-2.webp) ##### Select the audience For this flash sale, you want to target users who have shown interest in premium services but haven’t subscribed yet. Select **Send to segment**, and from the dropdown menu, choose the **Interested in Premium** segment. This segment includes users who have viewed the premium page but haven’t upgraded. ![Audience selection showing Send to segment option with Interested in Premium segment selected from dropdown](https://docs.pushwoosh.com/push-notifications-one-time-push-3.webp) ##### Set the timing Since the flash sale is time-sensitive, you want the notification to go out immediately. Select **Send immediately** to dispatch the notification as soon as the campaign setup is complete. ![Scheduling options showing Send immediately option selected for time-sensitive flash sale campaign](https://docs.pushwoosh.com/push-notifications-one-time-push-4.webp) ##### Review and send Review all the campaign details to ensure the notification looks correct. ![Review screen showing complete campaign configuration summary with push notification preview and settings](https://docs.pushwoosh.com/push-notifications-one-time-push-5.webp) ##### Link to aggregated campaign (optional) If this notification is part of a long-term marketing campaign with multiple messages (e.g., a seasonal sale or ongoing promotions), you can toggle the **Link to Aggregated Campaign** option. This will allow you to track this notification’s performance alongside other related messages in the broader campaign. Click **Send now**. In the **Confirm sending** dialog, enter **Flash_Sale_Push** in **Campaign name** so the send is easy to find in Message History, and click **Send now**. You can also send a one-time push using Customer Journey Builder. [Learn how to send one-time push via Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/). --- # One-time push via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/ Send an immediate one-time push to a segment using Customer Journey Builder with Audience-based Entry and Push elements. --- You can send a one-time push notification through Customer Journey Builder instead of the [one-time push form](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/). The process is the same for mobile and web. ## Build the journey When you create a push campaign in Customer Journey Builder, choose the **One-time campaign** template. It adds [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) and [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) to the canvas and connects them for you, leaving the branch without an outgoing connection — a valid way to end the journey. You can also build the journey manually by dragging these two elements onto the canvas and connecting them in order. ## Set the audience Double-click the **Audience-based Entry** element to choose who receives the push. In the same element you can set **Scheduled Launch** to send the push at a specific date and time. [Learn more about Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), including [Scheduled launch](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#scheduled-launch). If you already have a segment of opted-in users (e.g. Push Alerts Enabled), in **Audience Source**, select that segment. ![Audience-based Entry element showing Push Alerts Enabled segment selected in Audience Source dropdown](https://docs.pushwoosh.com/push-notifications-one-time-push-7.webp) If you need to create the segment: 1. Click **Create Segment**. 2. In the segment builder, add a filter: tag **Push Alerts Enabled** is true. 3. Click **Save segment**. [](https://docs.pushwoosh.com/push-notifications-one-time-push-8.webm "Segment creation animation showing creation of Push Alerts Enabled segment filter with tag condition") ## Configure the push and launch 1. Double-click the **Push** element and select or [create push content](https://docs.pushwoosh.com/product/content/push-presets/). [Learn more about the Push element in Customer Journey](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) 2. When the journey is ready, click **Launch campaign**. ![Customer journey interface showing Launch campaign button on the right side of the canvas](https://docs.pushwoosh.com/push-notifications-one-time-push-9.webp) --- # Send push to all user devices **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/push-by-user-id/ Send targeted push messages to users across their multiple devices using User ID to reach all associated devices simultaneously. --- This method allows you to send targeted push notifications to your users across their multiple devices. Resulting push notifications will be delivered to all devices associated with a specified User ID. [More about User IDs.](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids#all-devices-delivery) Developer assistance needed To begin sending these pushes, you need to collect UserIDs. You’ll need assistance from your development team for this. Please [share this link](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) with them to get started. ## Sending a push to all user devices with Customer Journey To send a push to all user devices, double-click the [**Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#send-by-user-id) element in your journey, toggle on **Send to all user devices**, and click **Apply**. ![Push element Options showing Send to all user devices toggle enabled in journey configuration](https://docs.pushwoosh.com/push-notifications-push-by-user-id-1.webp) Tip You can also send a push by User ID via API. To do this, share [this link](https://docs.pushwoosh.com/developer/guides/messaging-channels/send-a-pushby-userid) with your developer team. --- # Scheduled push **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/scheduled-push/ Schedule a one-time push for a specific date and time using the campaign form or Customer Journey Builder. Step-by-step instructions for both options. --- ## Schedule via the one-time push form To send a one-time push notification in Pushwoosh, go to the **Campaigns** section. Select **One-time messages** and click **Send message** → **One-time push**. ![Campaigns section showing One-time messages option with Send message button](https://docs.pushwoosh.com/shared-51.webp) ### Select or create push message content To start, either create new content or select existing content for your push notification. If you’d like to use a pre-built push preset, choose it from the dropdown menu of available options. ![Content step of the push form with the 20% Discount Voucher preset selected and an arrow pointing to the Sort: Last updated button above the preset dropdown](https://docs.pushwoosh.com/push-notifications-scheduled-push-2.webp) To change the order of presets in the dropdown, click the sort button above it. The button shows the current order, which is **Last updated** until you pick another one. Pushwoosh remembers the order you pick and uses it in every push preset dropdown you open: one-time and scheduled pushes, the [Push element in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), and [geozones](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/). To create a new message, click **Create new** and [follow this guide](https://docs.pushwoosh.com/product/content/push-presets) to set up a new push preset. Note The push preset you create here won’t be saved in the Push presets list to keep it organized and uncluttered. If you need to save a preset for future use, go to [Push presets](https://docs.pushwoosh.com/product/content/push-presets), create it in advance, and then select it from the dropdown menu. If you need to edit an existing preset, click **Edit Content** to modify the message. A preview of the notification will be displayed on the right side, showing how it will appear to users. Once you’re satisfied with the content and appearance, click **Next** to proceed to the Audience setup step. ### Select audience for push notification Next, choose the audience for your push notification. You can either send the notification to all users or target a specific segment based on user behavior or demographics. #### Send to segment Select this option to target a specific audience segment. Choose a pre-built segment from the dropdown menu, or create a new segment by clicking **Create Segment**. For more details, refer to the [guide on creating segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments). ![Audience selection screen showing Send to segment option with segment dropdown](https://docs.pushwoosh.com/shared-60.webp) #### Send to all users Choose this option to send the notification to all your subscribers. This is ideal for general announcements or promotions that apply to your entire user base. Important Ensure the message content is relevant to all recipients. Sending irrelevant or uninteresting notifications may result in user frustration or increased opt-outs. ![Send to all users option selected for push notification audience](https://docs.pushwoosh.com/shared-22.webp) #### Target platforms Select the platforms on which you want the notification to be delivered. Available platforms include: * iOS * Android * Safari * Chrome * Firefox Choosing target platforms ensures that your message reaches users on the appropriate devices. Only users on the selected platforms will receive the notification. ![Target platforms selection showing iOS, Android, Safari, Chrome, and Firefox options](https://docs.pushwoosh.com/shared-46.webp) Once you’ve configured your audience and selected the target platforms, click **Next** to proceed with setting up your push notification. ### Schedule push notification Next, choose **Selected time** to schedule your push notification for a specific date and time. 1. Use the calendar to select the exact date for your notification. 2. Set the time in hours and minutes (using the 24-hour format) for when the notification should be sent. 3. Choose the appropriate time zone: * **Subscriber’s device timezone:** the notification will be sent based on the recipient’s local time, ensuring delivery at the specified time in their timezone. * **Custom timezone:** select a specific timezone to schedule the notification for all recipients, useful for targeting users in a specific region. ![Schedule push notification screen showing date, time, and timezone selection options](https://docs.pushwoosh.com/shared-39.webp) ### Review and edit your content Before finalizing, review the content, on-click actions, audience, platform, and scheduling options to ensure everything is configured correctly. You’ll also see a preview of your push notification in each selected language. You can adjust any settings as needed. ![Review and edit content screen showing push notification preview and configuration summary](https://docs.pushwoosh.com/shared-15.webp) Optionally, you can choose to link your push notification to an **Aggregated Campaign**, allowing you to associate this message with a larger, ongoing campaign and track its performance alongside related notifications over time. ![Link to Aggregated Campaign option toggle for associating push notification with larger campaign](https://docs.pushwoosh.com/shared-56.webp) Click **Schedule push** or **Send now** to open a **Confirm sending** dialog with a **Campaign name** field. This is separate from linking to an Aggregated Campaign above. A campaign name only labels this one send. It doesn’t group it with other messages. Campaign name is prefilled with the push notification’s own title, or its body text if the title is empty. Clear it and Message History falls back to the same default, the same as before this field existed. Keep it to 255 characters or fewer. Typing past the limit truncates the text without a warning. ![Confirm sending dialog with Campaign name field prefilled from the push notification's body text, since this preset has no title](https://docs.pushwoosh.com/shared-63.webp) Once set, the campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the notification title. Click **Schedule push** or **Send now** again in the dialog to confirm. #### Cancel or edit a scheduled message For messages that are scheduled and not yet sent (status **Scheduled** in the list), you can change or cancel them. Open the actions menu (⋮) for the message in the one-time messages list: * **Delete message:** cancels the scheduled delivery and changes the status to **Stopped**. ![One-time message row with status Stopped after Delete message](https://docs.pushwoosh.com/push-notifications-scheduled-push-3.webp) * **Edit message:** opens the one-time push form with the message data. You can change content or audience (the scheduled send time cannot be changed). * **Rename:** opens a small dialog to set or change the campaign name, without reopening the full edit form. Same 255-character limit as the Campaign name field you set at send time. Clear the field and save to remove the name. The row reverts to its previous default title. ![One-time message row with actions menu open showing Edit message, Message details, and Delete message options](https://docs.pushwoosh.com/push-notifications-one-time-push-32.webp) While editing * When editing, you can update **content** and **audience** only. The scheduled send time is fixed and cannot be changed. To change the send time, cancel the message (**Delete message**) and create a new one. * On the **Audience** step you can change the segment, create a new one, or change the device list. The audience size is recalculated after you make a change. * If the message starts sending while you are editing, your changes can’t be saved: **Apply changes** shows an error, and the message goes out as it was. * In Review, the button is labeled **Apply changes** instead of **Schedule push** or **Send now**. Click it, then confirm again in the **Confirm sending** dialog to save your changes, including any edit to the campaign name. ## Schedule a push with Customer Journey Builder The process is the same for both mobile and web push notification campaigns. This guide explains how to schedule push notification delivery for a specific date and time. The scheduling setup may vary depending on whether the push notification starts a new communication flow or continues an existing one. ### If a push notification initiates a communication flow Once you’ve set up a [basic push flow](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/), you can add scheduling in the Entry element. In the [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#entry-schedule) element, set **Entry schedule** to **One-time entry**, then choose **Schedule for a specific day**. Set the date, time, and timezone. ![Audience-based entry: Schedule for a specific day with date, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-2.webp) Select the timezone. There are two options: 1. **Subscriber’s device timezone (default):** users enter based on their device timezone. If timezone is missing, a fallback is used. Click **Change fallback** to change it. 2. **Specific timezone:** pick a timezone from the dropdown for a fixed entry time. Note If a subscriber’s scheduled entry time (in their local timezone) has already passed, they will not be included in the campaign unless you choose to let them enter immediately. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#schedule-for-a-specific-day). Once scheduling is set, click **Apply**. Note Alternatively, you can set recurring pushes to be sent at regular intervals (daily, weekly, etc.) rather than on specific dates. Learn [how to set recurring pushes](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/autopushes). ### If a push notification continues a communication flow Insert the [**Time Delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) element between the preceding communication elements and the push notification you wish to schedule: [](https://docs.pushwoosh.com/shared-7.webm "Customer journey canvas showing Time Delay element inserted between communication elements") Double-click the **Time Delay** element. In the **Time Delay Option** field, select either **Specific time**: ![Time Delay element configuration showing Specific time option selected](https://docs.pushwoosh.com/shared-9.webp) or **Date**: ![Time Delay element configuration showing Date option selected for scheduling](https://docs.pushwoosh.com/shared-4.webp) Note Within the **Time Delay** element, you can set other delivery conditions: waiting for fixed durations, until a specific time or date, for specific days of the week, or for a delay based on user or event data. To wait until the user triggers an event, use the [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) element. [Learn more about how to use the Time Delay element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/). --- # Targeted push **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/targeted-push/ Learn how to send targeted push notifications to specific audience segments with Pushwoosh Customer Journey Builder. --- Note The flow is the same for mobile and web push notification campaigns. This flow shows how to send a targeted push notification _to a specific audience segment. The way you’ll build your segments for targeted pushes differs depending on whether a push notification initiates a communication flow or continues an existing one. ## If a push notification initiates a communication flow Once you’ve created [a basic push flow](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push-via-customer-journey/), you can set up your targeted push. Double-click the [**Audience-based Entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) element to select the audience for your push notification. Build a segment from scratch, choose from your existing segments, or import a segment file. Tip In the same element you can set when users enter (immediately or Schedule for a specific day) and allow users who match the segment later to enter. See [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#entry-schedule) for entry schedule and dynamic entry. #### 1. Build a segment from scratch Click **Create segment** and select **Build segment** from the dropdown. ![Segment creation screen showing Save segment button and configured filters](https://docs.pushwoosh.com/push-notifications-targeted-push-2.webp) In the drawer that opens, build the segment. [Learn how to create segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/). ![Segment creation screen showing Save segment button and configured filters](https://docs.pushwoosh.com/push-notifications-targeted-push-1.webp) #### 2. Select an existing segment Select one of your existing segments in the **Audience source** field. In the following example, a push will be targeted at paid customers: ![Audience source dropdown showing existing segments including paid customers option](https://docs.pushwoosh.com/push-notifications-targeted-push-3.webp) #### 3. Import a segment file In the **Audience source** section, click **Create segment** and select **Import segment** from the dropdown: ![Audience-based Entry modal showing Create segment dropdown with Build segment and Import segment options](https://docs.pushwoosh.com/push-notifications-targeted-push-5.webp) Then import your file in CSV format. For step-by-step instructions and file requirements, see [How to import a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment). ![Import segment dialog showing CSV file upload option](https://docs.pushwoosh.com/push-notifications-targeted-push-6.webp) Once you’ve selected the audience source and set up the element, click **Save**. [Learn more about Audience-based Entry ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) ## If a push notification continues a communication flow Insert the [**Condition split**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) element between the preceding communication elements and the push notification you wish to target. Double-click the element to open its configuration. ### Define the segment Under **What defines the user flow?**, select **Segment**. ![Customer journey canvas showing Condition split element inserted before push notification](https://docs.pushwoosh.com/push-notifications-targeted-push-8.webp) You can then set the segment in one of three ways: #### Select an existing segment Use the **Existing segment** field to choose one of your segments. ![Condition split element showing Existing segment dropdown with paid customers segment selected](https://docs.pushwoosh.com/push-notifications-targeted-push-10.webp) #### Build a segment from scratch Click **Create segment** next to the **Branches** section and select **Build segment** from the dropdown. A drawer opens where you can build the segment. [Learn how to create segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) #### Import a segment file Click **Create segment** and select **Import segment** from the dropdown. Then import your file in CSV format. For step-by-step instructions and file requirements, see [How to import a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment). ### Configure the branches The **Branches** section shows the available paths, such as **Users in a segment** (users who match the segment you set) and **All other users**. ![Branches section showing Users in a segment and All other users paths in Condition split configuration](https://docs.pushwoosh.com/push-notifications-targeted-push-9.webp) On the journey canvas, add the push notification to the **Users in a segment** branch. The **All other users** branch can be left without an outgoing connection to end it there, or you can add an **Exit** element or another step. ![Condition split element showing configured segment and Save button](https://docs.pushwoosh.com/push-notifications-targeted-push-11.webp) When done, click **Save**. --- # Understanding subscription prompts **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/ Use subscription prompts to convert website visitors into push notification subscribers and expand campaign reach. --- Superseded by the Subscription widget For an application with the Web platform in panel-managed mode, the [Subscription invitation and Subscription bell](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/subscription-widget/) cards on **Settings → Configure platforms → Web** replace the subscription prompt below. The prompt no longer loads there, and its settings on this page are not carried over to the new cards. Subscription prompts are a powerful tool to engage users by encouraging them to subscribe to your push notifications. These prompts can be customized to appear at strategic moments in the user journey, helping you build a larger, more engaged audience. By effectively using subscription prompts, you can ensure that your messages reach a wider audience, driving higher engagement and conversion rates. Developer assistance needed You’ll need your development team’s assistance to configure subscription prompts on your website. Share [this link](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/) with them to get started. ## The benefits of subscription prompts * Subscription prompts are an effective way to invite website visitors to opt-in to your push notifications, increasing the number of subscribers you can reach with your campaigns. * By choosing the right moment to display a subscription prompt, you can encourage users to stay connected with your brand, leading to better retention and more frequent interactions. * Customize the messaging and timing of your subscription prompts to align with user behavior, making the invitation to subscribe feel more relevant and appealing. There are two options available: * [**Default widget**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/default-widget/) simply triggers the native subscription prompt. * [**Custom widget**](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/custom-subscription-widget) allows you to create a unique user experience that fully fits your website. Developer assistance needed You’ll need your development team’s help to create and configure custom subscription prompts on your website. Share the link above with them to get started. --- # Default widget **URL:** https://docs.pushwoosh.com/product/messaging-channels/push-notifications/subscription-prompt/default-widget/ Boost push subscriptions with a no-code widget, quick setup, customizable design, and proven methods to grow your audience. --- The default subscription widget aims to encourage your website visitors to subscribe to push notifications. Shown to users based on your website’s logic, the widget gently prompts website visitors to allow receiving push notifications from you, thus creating long-term relationships with prospects and customers. **Use the default widget to:** * **Grow your audience**. Describe the benefits of receiving web push notifications to encourage existing customers to subscribe. * **Reduce spending on integration**. The default widget works out-of-the-box and needs almost no coding to be integrated into your website. ## How it works The default widget contains plain text explaining the value of push notifications and two buttons to agree or disagree on receiving pushes. When a user presses the Confirmation button, the native subscription prompt is triggered. Note **Frequency capping** If a user clicks **Deny** or ignores the default widget, our SDK will show the default widget again after **4 hours**. The widget is capped to **3 total impressions**. If a user denies three times in a row, they will not see the widget anymore and should change their opt-in preferences manually from browser settings. ## How to implement a default widget ### Integrate the default widget Developer assistance needed You’ll need your development team’s assistance to integrate the default widget. Share [the configuration guide link](https://docs.pushwoosh.com/developer/guides/messaging-channels/subscription-prompt/default-widget) with them to get started. ### Create a default widget To configure the subscription prompt, go to **Settings → Configure platforms → Web push notification → Subscription prompt**, then click **Settings**. ![Subscription prompt configuration screen](https://docs.pushwoosh.com/subscription-prompt-default-widget-1.webp) Then, select **Default widget** and customize the widget to contain the most explanatory and convincing text, clear CTA buttons, and colors fitting your website styles. ![Customizing default subscription prompt widget](https://docs.pushwoosh.com/subscription-prompt-default-widget-2.webp) To save changes, click **Save** at the bottom of the page. Then, click **Publish** at the top of the page to start showing the widget to website visitors. ![Saving and publishing subscription prompt changes](https://docs.pushwoosh.com/subscription-prompt-default-widget-3.webp) > If you’d like to remove the widget from your site, select the **My custom widget** option and click **Publish**. --- # Getting started with SMS messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/ Improve customer communication with fast, reliable SMS messaging for alerts, promotions, and real-time engagement. --- ## Why use SMS in your campaigns? SMS is a powerful channel that allows you to reach customers effectively, especially when other messaging options are unavailable. With SMS, you can: * **Contact customers who are not reachable via other channels.** If a customer is not subscribed to push or email notifications, you can send an SMS instead. * **Send transactional notifications,** like order statuses, payment alerts, and security codes. * **Send marketing offers.** For customers who have agreed to receive promotional SMS, this channel can be even more effective than push notifications and emails. Like push notifications, each SMS can be personalized by using [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content). ## Prerequisites for sending SMS 1. **Connect your SMS provider.** Configure the SMS channel by connecting your provider in **Settings → Configure Platforms → SMS**. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) 2. **Import SMS contacts.** [Upload a CSV file with phone numbers](https://docs.pushwoosh.com/product/messaging-channels/sms/import-sms-contacts/). ## Create an SMS preset (optional) If you plan to use a text preset, create it in the **Content → SMS presets** section. [Learn more about creating SMS presets ](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) ### Personalize SMS for better engagement Enhance user experience by sending personalized SMS tailored to user behavior and preferences. [Learn more about personalized SMS ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-sms/) ## Send SMS Send SMS through the one-time SMS form or automate delivery with Customer Journey. [Learn more about sending SMS ](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/) ## Send RCS messages through Twilio With Twilio on your own account, an SMS preset can carry a Twilio content template that phones receive as a branded RCS message with cards, carousels, or buttons. Phones without RCS get an SMS built from the template. [Learn more about RCS messages ](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) ## Marketing consent Marketing SMS aren’t sent to phone numbers that opted out, for example, by replying `STOP`. [Learn more about SMS and Viber marketing consent ](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/) ## Track and analyze message performance To view statistics for each message, go to the [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history) section. Caution Please note that some mobile operators do not transmit delivery status data, so the statistics may need to be more accurate. You can check the details with the mobile operators that provide numbers to your customers. --- # SMS and Viber marketing consent **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/ Record whether a phone number agreed to marketing SMS and Viber messages, and stop marketing sends to numbers that opted out. --- Marketing consent records whether a phone number agreed to receive marketing SMS or Viber messages. Pushwoosh sends a marketing message only to numbers that can receive it. In opt-out mode, that’s everyone who hasn’t opted out. In opt-in mode, it’s only those who agreed. [Transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) skip this check. ## Consent tags Pushwoosh keeps consent in two Boolean tags, one for each channel. | Tag | Channel | Values | | --------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SMS Marketing Consent** | SMS | `true`: the phone number agreed to marketing SMS. `false`: the phone number opted out. No value: Pushwoosh doesn’t know whether the phone number agreed. | | **Viber Marketing Consent** | Viber | `true`: the phone number agreed to marketing Viber messages. `false`: the phone number opted out. No value: Pushwoosh doesn’t know whether the phone number agreed. | Each subscriber has its own value in each application. A phone number registered for both SMS and Viber is two subscribers, so consent on one channel doesn’t change the other. Pushwoosh adds the tag to your account the first time a value is saved: when a phone number first opts out, or when you first set it with `setTags`. After that, the tag appears on the [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) page. Use it in [segment filters](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) like any other Boolean tag, for example, to count subscribers who agreed to marketing. ## Which messages are blocked Each application works in one of two modes, set separately for SMS and Viber: * **Opt-out mode (default):** marketing messages go to every subscriber except those with the tag set to `false`. * **Opt-in mode:** marketing messages go only to subscribers with the tag set to `true`. | Tag value | Marketing message | Transactional message | | --------- | -------------------------------------------- | --------------------- | | `true` | Sent | Sent | | `false` | Not sent | Sent | | No value | Sent in opt-out mode Not sent in opt-in mode | Sent | A message is transactional when you choose **Transactional message** in the [SMS](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#set-message-type) element, in the [one-time SMS form](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/#select-message-type), or in the [Viber](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#set-message-type) element. In the API, set the message type to transactional: [`message_type`](https://docs.pushwoosh.com/developer/api-reference/messages-api/api-prerequisites/#message_type) in `/createMessage` for SMS, or `MESSAGE_TYPE_TRANSACTIONAL` in [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) for SMS and Viber. Pushwoosh treats every other message as marketing, including SMS sent without a message type. Pushwoosh checks consent before it applies the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). A subscriber who can’t receive the message isn’t counted in the control group of that message. ## Switch to opt-in mode Caution In opt-in mode, subscribers without a tag value stop getting marketing messages. Record consent for every subscriber who agreed before you switch. 1. Set **SMS Marketing Consent** or **Viber Marketing Consent** to `true` for every subscriber who agreed to marketing messages. See [Record consent](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/#record-consent). 2. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). Name the application and the channels to switch. You can’t change the mode in the Control Panel. ## Record consent Developer assistance needed You’ll need help from your development team to record consent. Ask your developers to set the tag with the [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) API method when a person agrees to or declines marketing messages, for example, on a signup form. The request needs the code of the application you send from and the subscriber’s `hwid`: * SMS: the phone number in E.164 format, for example, `+15551234567`. * Viber: the phone number in E.164 format with the `viber:` prefix, for example, `viber:+15551234567`. The phone number must already be [registered](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) as an SMS or Viber subscriber in that application. Pushwoosh never sets the tag to `true` by itself. A reply such as `START` doesn’t restore consent. Only your developers’ `setTags` request does. ## Opt-outs Pushwoosh records automatically Pushwoosh sets the tag to `false` when a person opts out in one of these ways: * **Reply by SMS or Viber:** the reply is exactly `STOP`, `STOPALL`, `UNSUBSCRIBE`, `CANCEL`, `END`, or `QUIT`. Upper or lower case and a final `.` or `!` are accepted. * **Opt-out at the mobile operator:** the SMS provider rejects a message because the person unsubscribed with their mobile operator. Pushwoosh records Twilio error `21610` and SMSAPI.ro errors `57` and `59`. * **Unsubscribe in Viber:** the person unsubscribes from your business account in Viber, and MStat reports it through the Viber subscription webhook. A longer reply or a reply in another language, such as “please stop the delivery”, doesn’t change the tag. Pushwoosh gets replies from Twilio, MStat, and CapitoleMobile after their incoming webhook is [set up](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/sms-provider-webhooks/). After any of these opt-outs, Pushwoosh also unsubscribes the phone number from the channel: * The number gets no messages on that channel, including transactional ones. * If you register the number again, or the person sends your SMS number any reply other than an opt-out word, transactional messages resume. * Marketing messages stay blocked until you set the tag to `true`. ## Blocked messages in statistics A subscriber skipped for missing consent gets status code `1038` for SMS or `1039` for Viber. The delivery funnel counts these subscribers as unsubscribed and excludes them from the audience. --- # Import SMS contacts **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/import-sms-contacts/ Upload a CSV file to register SMS subscribers, segment audiences, and personalize your SMS campaigns. --- You can upload a CSV file to bulk import phone numbers. Imported numbers will be registered as SMS subscribers, allowing you to personalize and segment your SMS communications. ## How to import SMS contacts from a CSV file SMS contacts are imported through the shared [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) wizard, which can register email, SMS, WhatsApp, and Viber subscribers from the same CSV file in one pass. Map the column with phone numbers to the **SMS** channel to register them as SMS subscribers. --- # RCS messages through Twilio **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/ Send RCS messages with cards, carousels, and buttons from SMS presets through your own Twilio account, with an SMS fallback. --- RCS (Rich Communication Services) messages arrive in the phone’s built-in messaging app as a chat from your brand, with your logo and a card, a carousel, media, or buttons instead of plain text. Pushwoosh sends them through Twilio as part of the SMS channel. Note Setup takes time: according to Twilio, carriers approve an RCS sender in 4–6 weeks or more per country, so start the registration early. ## How it works An RCS message gets from a Twilio template to the phone in three steps: 1. You build the message as a content template in Twilio. 2. You pick the template in an [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#send-over-rcs). Each language of the preset can carry its own template. 3. When the preset is sent, Twilio tries to deliver the template over RCS. Phones that can’t receive RCS get an SMS instead, as described in [SMS fallback](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/#sms-fallback). If you pick a template for only some languages of the preset, the other languages aren’t sent over RCS. They go out as a regular SMS with the text from their **Message** field. ### SMS fallback The SMS fallback is what phones without RCS receive instead of the RCS message. If the recipient’s phone or carrier doesn’t support RCS, Twilio sends an SMS built from the template’s **Text** type. The SMS fallback comes from the template, not from the **Message** field of the preset. If the template has no **Text** type, phones without RCS don’t get the message at all. ## What you need to send RCS RCS works only through your own Twilio account with a Messaging Service and a carrier-approved RCS sender. * **Twilio as the SMS provider.** Connect it with your own Twilio **Account SID** and **Auth token**. If Pushwoosh provides the Twilio number and credentials for your project, RCS isn’t available, and such sends are rejected. * **A Twilio Messaging Service.** Its sender pool holds an RCS sender and an SMS number. Its SID goes into **Messaging Service SID** in the SMS platform settings. Without it, a message with a template fails to send. * **A From number.** Set it in the SMS platform settings to the SMS number from the Messaging Service pool. Pushwoosh doesn’t send Twilio SMS without it, even when a Messaging Service is set. * **An RCS sender approved by carriers** in the countries you send to. Twilio handles the registration. According to Twilio, it takes 4–6 weeks or more per country. ## Set up RCS in Twilio These steps run in the Twilio Console. Menu names follow the [Twilio RCS onboarding guide](https://www.twilio.com/docs/rcs/onboarding) and may differ slightly in your console. ### Create an RCS sender The RCS sender is the brand profile recipients see in the chat: your name, logo, and colors. 1. Go to **Communications → Numbers & senders → Overview → RCS**. 2. Click **Create RCS Sender**. 3. Enter a **Sender display name**. 4. Accept the terms and click **Continue**. 5. On **Public details**, fill in every field: * **Display name** and **Description**. * **Logo image** (224×224 px, up to 50 KB) and **Banner image** (1440×448 px, up to 200 KB), as public JPG or PNG URLs. * **Accent color** with a contrast ratio of at least 4.5:1 against white, for example `#1A73E8`. * A contact phone number or email, a **Privacy policy URL**, and a **Terms of service URL**. 6. Click **Next**. ### Add a test phone Until carriers approve the sender, it delivers only to test phones. 1. On **Try it out**, click **Add device to test this sender**. 2. Enter an Android phone number in international format and click **Invite**. 3. On the phone, open the invitation in Google Messages and tap **Make me a tester**. 4. In the console, confirm that the owner accepted the invitation. 5. Send a message from **Your message**. It should arrive as a chat from your brand with the logo, not as an SMS. ### Register the sender with carriers Twilio adds an RCS sender to a Messaging Service only after at least one carrier has approved it, so start the registration before you build the Messaging Service. 1. On the RCS sender, open **Compliance registration**. 2. Click **Add countries**. 3. Select the countries you send to and click **Save countries**. 4. Click **Start**. 5. Fill in the form and click **Submit registration**. For the US, Twilio has a separate **US registration** form. The registration status shows as **Pending Review** until carriers approve it. ### Create a Messaging Service The Messaging Service groups the RCS sender and an SMS number, so Twilio can choose RCS or SMS for each phone. 1. Go to **Messaging → Services**. 2. Create a service. 3. In **Sender Pool**, add an SMS-capable Twilio number. Without it, phones without RCS get nothing. 4. Once at least one country shows **Approved** in **Compliance registration**, add the RCS sender to **Sender Pool**. 5. Copy the service SID. It starts with `MG`. ### Build a content template The content template defines the RCS message and its SMS fallback. 1. Go to **Messaging → Content Template Builder**. 2. Click **Create new**. 3. Choose an RCS-capable type: **Card**, **Carousel**, **Quick reply**, **Call to action**, or **Media**. 4. Fill in the content. For a value that changes per recipient, use a numbered variable, for example `{{1}}` for the first name in the card title. 5. To branch a journey on the reply, add quick reply buttons and give each one an ID. Don’t use `default` or an ID that starts with `output`: such a button gets no branch in the journey. 6. Add the **Text** type to the same template. Twilio builds the SMS fallback from it. 7. Save the template. ## Connect Twilio in Pushwoosh Pushwoosh sends RCS with the Twilio credentials and the Messaging Service you enter in the SMS platform settings. 1. Go to **Settings → Configure Platforms**. 2. Open **SMS**. 3. Set the SMS provider to **Twilio**. 4. Fill in the fields: * **From number:** the SMS number from the Messaging Service pool. * **Account SID** and **Auth token:** the credentials of the Twilio account that owns the RCS sender and the templates. * **Messaging Service SID (optional):** the `MG` SID of the Messaging Service. The field is optional for SMS, but RCS needs it. 5. Click **Apply**. For the rest of the SMS settings, see [SMS platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/). Delivery statuses of RCS messages need no webhook setup, because Pushwoosh submits the status callback URL to Twilio with every message. ## Receive replies Replies to RCS messages reach Pushwoosh only through a webhook you set on the RCS sender. 1. In the Twilio Console, open the RCS sender and go to **Configuration**. 2. Set **Webhook URL for incoming messages** to the incoming SMS URL from [SMS provider webhooks](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/sms-configuration/sms-provider-webhooks/#automatic-setup), with the `POST` method. 3. Click **Save configuration**. Pushwoosh handles an RCS reply the same way as an SMS reply. It fires the `PW_SMSReceived` event for the sender’s phone number, and a `STOP` reply sets the subscriber’s marketing consent tag to `false`. See [SMS and Viber marketing consent](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/). ## Send an RCS message An RCS message goes out wherever you send an SMS preset that has a template. 1. Open or create an SMS preset. 2. Pick a Twilio template for each language in the **RCS** block, as described in [Send over RCS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#send-over-rcs). 3. Send the preset in an [SMS campaign](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/) or a [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-sms-via-customer-journey/). In a journey, an SMS element with a quick reply template gets a branch for each button and waits up to 24 hours for a tap. See [Branch on RCS reply buttons](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#branch-on-rcs-reply-buttons). ## Check RCS statistics To see how many deliveries were confirmed over RCS, how many arrived as SMS, and what share of RCS recipients read the message, open the [Message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#overview-tab) of the send. --- # How to send SMS **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/ Send SMS via one-time campaigns or Customer Journey. Choose immediate delivery or automated multi-channel flows. --- Before you send SMS, ensure you have [configured your SMS provider](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) and added recipients by [importing SMS contacts](https://docs.pushwoosh.com/product/messaging-channels/sms/import-sms-contacts/) or [registering phone numbers via API](https://docs.pushwoosh.com/developer/first-steps/subscribe-users/#sms). Pushwoosh offers two ways to send SMS: #### One-time SMS Send a single SMS to a segment, list of phone numbers, or all subscribers. Schedule it for now or a specific date and time. [Send one-time SMS ](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/) #### SMS element in Customer Journey Add an SMS step to a journey and combine it with push, email, in-app, or WhatsApp. Trigger delivery by audience or events. [Send SMS via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-sms-via-customer-journey/) Tip Use **one-time SMS** for single campaigns and **Customer Journey** for automated, multi-channel flows. #### Send SMS via API Your development team can send SMS programmatically with [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). [Messaging API v2 ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) --- # Send one-time SMS **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/ Send a single SMS to a segment, a list of phone numbers, or all subscribers. Choose a preset, message type, audience, and schedule in Control Panel. --- Before you start Connect your SMS provider in **Settings → Configure Platforms → SMS** before you create a campaign. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) To send a one-time SMS in Pushwoosh, navigate to the **Campaigns** section, select **One-time messages**, and click **Send message** > **SMS**. ## Select SMS content In the **Content** step, choose the [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) you want to send. The message text comes from the preset, so a preset is required. * To find an existing preset, use the search field or open the dropdown and select a preset. The preview on the right shows how the message will look, including thumbnails of any [MMS attachments](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-mms-attachments) the preset has. * To create a new preset, click **Create new**, build the preset, and save it. The new preset is then selected automatically. ![SMS preset field with Weekend flash sale selected and an arrow on the Name A→Z sort button](https://docs.pushwoosh.com/messaging-channels-sms-one-time-2.webp) To change the order of presets in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every SMS preset dropdown, in one-time messages and journeys alike. ### Select message type Choose how this SMS is treated for subscription and delivery rules: * **Marketing message**: Respects frequency capping. Not sent to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: Sent to all users regardless of subscription status. Bypasses frequency capping. Delivered to control group users. ![Marketing message and Transactional message options in the SMS campaign form](https://docs.pushwoosh.com/messaging-channels-sms-one-time-3.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Select the audience for your SMS campaign In the **Audience** step, define who will receive your SMS. ![SMS audience step with Send to segment, list of phone numbers, and all subscribers options](https://docs.pushwoosh.com/messaging-channels-sms-one-time-4.webp) ### Send to segment Target a predefined [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of your audience based on tags, events, or behavior. Select a segment from the dropdown, or click **Create segment** to [build](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) or [import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment) one. ### Send to a list of phone numbers Send the SMS to a specific list of phone numbers entered directly in the form. Phone numbers that are not yet in your project are automatically registered as SMS devices and included in the send. ### Send to all subscribers Target your entire SMS-reachable audience. Once you’ve set your audience, click **Next** to proceed to the scheduling step. ## Schedule SMS Choose when to send your SMS. ### Send immediately Send the SMS as soon as the campaign setup is complete. This is ideal for urgent or time-sensitive messages. ### Selected time Schedule your SMS for a specific date and time. * **Set the date and time**: Use the calendar to pick the date, then specify the time in hours and minutes (24-hour format). * **Select the timezone**: * **Subscriber’s device timezone**: The SMS is sent at the specified local time for each recipient. * **Custom timezone**: The SMS is sent at the same time for all recipients, useful for targeting a particular region. ![SMS scheduling with Send immediately and Selected time options, date picker, and timezone selection](https://docs.pushwoosh.com/messaging-channels-sms-one-time-5.webp) ### Configure message delivery settings #### Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive SMS messages, preventing over-messaging and reducing opt-outs. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is 3 messages in 9 days, additional messages exceeding this limit are skipped. ![Use global frequency capping settings option selected in message delivery configuration](https://docs.pushwoosh.com/push-notifications-one-time-push-25.webp) * **Send messages without global frequency capping** The user receives this message even if they’ve exceeded the channel’s message limits. Use with caution to avoid over-messaging. ![Send messages without global frequency capping option selected in message delivery settings](https://docs.pushwoosh.com/push-notifications-one-time-push-26.webp) * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message is skipped. Under **Counting mode**, choose how sent messages are counted: * **Rolling window**: counts the messages sent within the last N×24 hours before each send, where N is the number of days in the limit. For example, with a limit of 3 messages per 2 days, a message sent Monday at 9 AM stops counting on Wednesday at 9 AM. * **Calendar day**: counts the messages sent today and on the previous N-1 calendar days, starting at midnight. With the same limit of 3 messages per 2 days, on Wednesday every message sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. Select a **Capping timezone** to set when midnight falls. If you don’t select one, calendar days are counted in UTC. ![Use custom frequency capping with message limit fields and Counting mode set to Calendar day with a Capping timezone selector](https://docs.pushwoosh.com/push-notifications-one-time-push-27.webp) Note Frequency capping applies only to **Marketing** messages. When you choose **Transactional**, frequency capping doesn’t apply and these options are disabled. #### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages are sent immediately. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings option selected in send rate configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid delivery spikes. ![Send messages without global send rate option selected for maximum delivery speed](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Specify the number of messages sent per minute for full control over delivery speed. ![Use custom send rate option with input field for messages per minute configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) Once you’ve configured the options, click **Next** to proceed to the confirmation step. ## Review and send In **Review**, check the preview and the summaries for content, audience, and schedule. If the selected preset has MMS attachments, the content summary shows “Sent as MMS with N image(s)” and the preview includes the attachment thumbnails. Update anything that still needs to change before you send. ![SMS campaign review step showing message preview, audience, and schedule summary before sending](https://docs.pushwoosh.com/messaging-channels-sms-one-time-6.webp) Click **Schedule** or **Send Now** to open a **Send this SMS?** dialog with a **Campaign name** field: a free-text label for this one send. Campaign name is prefilled with the SMS preset’s own body text (a multi-line message prefills as a single line). Clear it and Message History falls back to naming the row after the preset, the same as before this field existed. Keep it to 255 characters or fewer. Typing past the limit truncates the text without a warning. ![Send this SMS confirmation dialog with Campaign name field prefilled from the SMS preset's body text](https://docs.pushwoosh.com/sms-one-time-confirm-sending.webp) Once set, the campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the preset name. Confirm again in the dialog to finish, depending on your selected scheduling options. ## Cancel or edit a scheduled SMS For SMS messages that are scheduled and not yet sent (status **PENDING**), you can change or cancel them. Open the list of messages in **Campaigns** → **One-time messages**, then open the actions menu (⋮) for the message: * **Edit message**: opens the SMS form with the message data loaded. You can update the content, audience, schedule, and delivery settings. In Review, the button is labeled **Apply changes** instead of **Schedule** or **Send Now**. Click it, then confirm again in the **Send this SMS?** dialog to save your changes, including any edit to the campaign name. * **Rename**: opens a small dialog to set or change the campaign name, without reopening the full edit form. Same 255-character limit as the Campaign name field you set at send time. Clear the field and save to remove the name. The row reverts to its previous default title. * **Delete message**: cancels the scheduled delivery and changes the status to **Canceled**. Tip You can also send SMS using [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["SMS"]`. This requires assistance from your development team. ## Example scenario Imagine you run an e-commerce store and want to send a flash-sale alert by SMS to customers who purchased in the last 90 days, scheduled for the morning the sale starts. **1.** Navigate to **Campaigns**, select **One-time messages**, click **Send message**, and choose **SMS**. **2.** In the **Content** step, select the **Flash Sale Alert** preset: `"Flash sale starts now! Get 30% off everything today only. Shop: {shortened link}"` Keep the message type as **Marketing** so it respects frequency capping. **3.** In the **Audience** step, choose **Send to segment** and select **Recent buyers (90 days)**. **4.** In the **Schedule** step, choose **Selected time**, set the date and time to the sale’s start, and select **Subscriber’s device timezone** so each customer receives it at the same local time. **5.** In **Review**, check the preview and the audience and schedule summaries, then click **Schedule** to finalize the campaign. --- # Send SMS messages via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-sms-via-customer-journey/ Send SMS using Customer Journey Builder. Configure preset, message type, split flow, and delivery settings. --- To send SMS using [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), add the SMS element to your journey and configure content, message type, delivery settings, and the optional split flow. Developer assistance needed To send SMS using Customer Journey Builder, first ask your developer team to associate customer phone numbers with **UserId** using the [/registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registerdevice) method. The phone number must be specified in the “hwid” parameter. ## Add the SMS element In Customer Journey Builder, add the [SMS element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/) to the canvas. ![Screenshot showing the SMS element being added to Customer Journey Builder canvas](https://docs.pushwoosh.com/messaging-channels-sms-2.webp) ## Configure SMS content Choose an SMS preset, or click **Create content** to build a new one. To set up presets in advance, see [SMS presets](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/). The preset list starts with the newest presets. To change the order, click its sort button. ![SMS element Preset field with Cart reminder selected and an arrow on the Newest first sort button](https://docs.pushwoosh.com/shared-30.webp) ## Choose the message type In the SMS element, select **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. All rules and limits apply. Respects: Frequency capping and Silence period. Excludes: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message**: For operational or service messages. Rules and limits don’t apply. Bypasses: Frequency capping. Sent to: the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![SMS element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/messaging-channels-sms-4.webp) ## Overwrite message personalization (optional) When you select a preset, you can overwrite how the message is personalized: * **Personalise message with event attributes:** Enable this option to use event attributes or API-based entry placeholders instead of device tags as value sources in message content and presets. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Overwrite Liquid placeholders:** Enable this option to use values from the selected Event in Liquid personalization. Liquid tags that match event attribute names will use values from the event. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) * **Use Vouchers:** Enable this option to use vouchers in message content. [Learn more](https://docs.pushwoosh.com/product/content/vouchers/) ![SMS element overwrite personalization options showing event attributes, Liquid placeholders, and vouchers toggles](https://docs.pushwoosh.com/messaging-channels-sms-5.webp) ## Configure message delivery settings ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to control how often users receive SMS messages, helping prevent message fatigue and opt-outs. You can choose one of the following options: * **Use global frequency capping settings** Apply the project-wide SMS limits set in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). *Example:* If the global limit is 3 SMS messages in 9 days, any additional messages will be skipped. * **Send messages without global frequency capping** Send the SMS regardless of how many messages the user has already received. Use with caution to avoid over-messaging and compliance issues. * **Use custom frequency capping** Set a custom SMS limit specifically for this message step. If the user has reached the limit, they will skip this message and move to the next step in the journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#customize-capping-for-specific-messages) ### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages will be sent immediately. Use this option when you want delivery speed to follow your project’s default rules. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid overloading your backend or creating delivery spikes. ![Send messages without global send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Allows you to specify the number of messages sent per minute, giving you full control over delivery speed. Messages will be sent at the custom rate you define in the message element. ![Use custom send rate](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) ## Flow options Enable the **Split flow depending on whether this message is delivered or not** toggle to split the journey based on whether the SMS is delivered. Then, set the delivery waiting time. If the SMS is delivered within the specified time period, the `"delivered"` branch will be activated. Maximum 7 days. ![Screenshot showing SMS delivery split flow configuration and waiting time settings](https://docs.pushwoosh.com/messaging-channels-sms-3.webp) ### Branch on RCS reply buttons When the selected SMS preset has an [RCS](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) template with quick reply buttons, the element branches on the button the user taps instead of on delivery. It gets a branch for each button, labeled with the button text, plus a **Not match** branch, and the canvas shows **Wait for reply: 24 hours** under it. ![SMS element Confirm delivery slot with Yes, No, and Not match branches and Wait for reply: 24 hours](https://docs.pushwoosh.com/shared-65.webp) * The user waits up to 24 hours for a tap. A tap sends the user down that button’s branch. If there’s no tap within 24 hours, or the phone doesn’t support RCS, the user goes to **Not match**. * The **Split flow depending on whether this message is delivered or not** toggle isn’t available, because the buttons branch the element. * A button whose text is an opt-out keyword (**STOP**, **STOPALL**, **UNSUBSCRIBE**, **CANCEL**, **END**, or **QUIT**) unsubscribes the user. Its branch stays on the canvas, but a tap on it never sends the user down that branch. * Only quick reply buttons get branches. A button that opens a link doesn’t. Caution If two RCS elements wait for a reply from the same phone number at the same time, a tap goes to the element that sent the most recent message. The other element moves the user to **Not match** after 24 hours. The branches come from the copy of the buttons Pushwoosh saves with the preset. If you change the buttons in Twilio, open the preset and save it again to update the branches. Tip You can also send SMS using [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with `platforms: ["SMS"]`. For this, you’ll need assistance from your development team. ### Finalize and save the journey Complete the journey setup, ending each branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. If you split the flow, set up actions for users routed to the other branch. Click **Launch campaign** to run the [Pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check) and fix any listed issues, then confirm by clicking **Launch**. --- # Getting started with Telegram **URL:** https://docs.pushwoosh.com/product/messaging-channels/telegram/ Connect a Telegram bot to Pushwoosh with BotFather, verify your API token, and use Telegram in your project. --- Telegram lets you reach users in a familiar channel where they already read and respond to messages quickly. With a Telegram bot connected to Pushwoosh, you can automate onboarding, reminders, and follow-ups in **Customer Journey Builder**. ## Start working with Telegram 1. **Create a bot in Telegram**\ Use **@BotFather** to create a new bot with `/newbot` or to copy an existing bot token with `/mybots`. You need the **API token** string BotFather provides. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/) 2. **Configure Telegram in Pushwoosh**\ Connect your Telegram bot to Pushwoosh and complete the platform setup. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/) 3. **Send Telegram messages in a journey**\ Use Customer Journey Builder to create and launch Telegram message flows. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/) Note After a user sends `/start` to your bot, Pushwoosh creates a profile in **User Explorer** with tags and events. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/#view-user-data-in-user-explorer) --- # Send Telegram messages in Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/ Learn how to send Telegram bot messages in Pushwoosh Customer Journey, configure the Telegram element, and track delivery results. --- Use this guide to send Telegram messages in [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Using Telegram through Pushwoosh helps you run Telegram as part of your omnichannel journeys, segmentation, and analytics in one place. ## Before you begin 1. Create a Telegram bot in **@BotFather** or use an existing one. 2. Configure Telegram in **Settings > Configure platforms** and verify the token. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/) Important Each project can use one Telegram bot. If you change the bot API token, active Telegram Journeys will be paused. ## Create a journey 1. Add the [**Trigger-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element. 2. Select **TelegramBotStarted** as the event. ![Journey canvas with Trigger-based entry configured for the TelegramBotStarted event.](https://docs.pushwoosh.com/messaging-channels-telegram-send-telegram-via-customer-journey-1.webp) Note The **TelegramBotStarted** event is fired when a user sends `/start` to your bot. A Telegram journey can start only after that. 3. Add the **Telegram** element to the canvas. Tip To split the flow by Telegram availability before sending, add **Reachability check** for Telegram. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) ## Configure message content In the Telegram element: 1. Enter the element name. 2. Enter your message in **Message text**. Keep the message text-only and within 4096 characters. 3. Optional. Add tags or Liquid placeholders to personalize content. [Dynamic content and Liquid in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys/) 4. Optional. Turn on **Use vouchers**, select **Voucher pool**, and set **Assign tag**. [Learn more](https://docs.pushwoosh.com/product/content/vouchers/) 5. Click **Apply**. ![Telegram element settings with message text, voucher options, and the Apply button.](https://docs.pushwoosh.com/messaging-channels-telegram-send-telegram-via-customer-journey-2.webp) ## Finalize and launch the journey Complete the journey, ending each branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. When the campaign is ready, click **Launch campaign**. ## Reachability and statistics If sending fails because of a temporary network issue, Pushwoosh retries automatically. If Telegram reports that the user blocked or removed the bot, that the chat wasn’t found, or that the account was deactivated, Pushwoosh unsubscribes the user from Telegram. These sends appear as **Chat Unavailable** (410007) on the **Errors** tab of the [message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#errors-tab). Nothing needs fixing: the person can’t receive messages from the bot anymore. See [Telegram errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/#telegram-errors). [Learn more about Telegram statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#telegram) ## View user data in User Explorer When a user starts a conversation with your bot, Pushwoosh creates a profile in **User Explorer** with **Telegram** as the platform and the Telegram user ID. ![User Explorer showing a Telegram user profile with device data and user tags after bot start.](https://docs.pushwoosh.com/messaging-channels-telegram-send-telegram-via-customer-journey-3.webp) The profile includes: * user tags received from Telegram * events such as `PW_TelegramBotStarted`. ![Events History in User Explorer showing Telegram bot start and received message events.](https://docs.pushwoosh.com/messaging-channels-telegram-send-telegram-via-customer-journey-4.webp) --- # Getting started with Viber messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/viber/ Send Viber messages via Customer Journey using Rakuten-approved templates or freeform text. --- Viber is a widely used messaging platform and offers a way to connect with your users. With Pushwoosh, you can send Viber messages as part of your marketing campaigns through Customer Journeys, using either a Rakuten-approved template or freeform text content. ## Prerequisites for sending Viber messages To start sending Viber messages with Pushwoosh, make sure the following requirements are met: 1. **Connect Viber to Pushwoosh** Link your Viber account to your Pushwoosh project by adding your auth token in **Configure platforms**. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/viber-configuration/) 2. **Prepare message content** To send a template message, make sure the template has already been submitted to and approved by Rakuten Viber. Only approved templates can be used as **Template** content in the Viber element and are billed at the lower Transactional rate. Freeform text doesn’t require approval, but is billed at the Promotional rate. Note Viber message templates are created and approved directly by **Rakuten Viber**, not in the Pushwoosh Control Panel. Important Starting July 1, 2026, Rakuten Viber requires an approved template for all transactional/OTP messages. Freeform content sent for transactional use cases is billed at the Promotional rate. ## Marketing consent Marketing Viber messages aren’t sent to phone numbers that opted out, for example, by replying `STOP` or unsubscribing in Viber. [Learn more about SMS and Viber marketing consent ](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/) ## Send Viber messages via Customer Journey Send Viber messages via Customer Journey using an approved Rakuten Viber template or freeform text, with an independent Marketing/Transactional message type setting. [Learn more ](https://docs.pushwoosh.com/product/messaging-channels/viber/send-viber-messages/) --- # Register Viber contacts **URL:** https://docs.pushwoosh.com/product/messaging-channels/viber/register-viber-contacts/ Learn how to register phone numbers as Viber subscribers in Pushwoosh --- For a customer to receive Viber messages, they must have a valid phone number and be registered as a Viber platform subscriber in Pushwoosh. This guide explains how to register contacts by importing a CSV file. Automatic registration When a contact messages your Viber Business account for the first time, Pushwoosh registers their phone number as a Viber subscriber automatically. ## How to import Viber contacts from a CSV file Viber contacts are imported through the shared [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) wizard, which can register email, SMS, WhatsApp, and Viber subscribers from the same CSV file in one pass. Map the column with phone numbers to the **Viber** channel to register them as Viber subscribers. Enter the plain phone number in [**E.164**](https://en.wikipedia.org/wiki/e.164) format. Pushwoosh adds the `viber:` prefix internally. You can then use this data to create segmented and personalized Viber campaigns via Customer Journey. [Learn more about sending Viber messages ](https://docs.pushwoosh.com/product/messaging-channels/viber/send-viber-messages/) --- # Send Viber messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/viber/send-viber-messages/ Send Viber messages via Customer Journey or API using Rakuten-approved templates or freeform text. --- This guide walks you through how to send Viber messages in Pushwoosh via Customer Journey or API. ## Send Viber messages via Customer Journey Pushwoosh allows you to integrate Viber messaging into [Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Follow these steps to configure it: 1. Go to **Campaigns > Customer Journey Builder** in the main sidebar. 2. Click **Build a journey from scratch**. 3. Add an **Entry** element to define who enters the journey. [Learn how to configure the Entry elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) 4. Add a [**Viber** element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/) and connect it to the rest of your flow (e.g., entry trigger and next steps). ![Viber element added to Customer Journey canvas and connected to the entry trigger and next steps.](https://docs.pushwoosh.com/viber-send-viber-messages-1.webp) 5. Click the **Viber** element to open its settings panel. Choose **Template** or **Freeform** content, set the **Message type**, configure **Frequency capping**, and optionally add **Vouchers**. [Learn more about the Viber element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/) ![Viber element settings panel with Template and Freeform options, message type, and frequency capping.](https://docs.pushwoosh.com/viber-send-viber-messages-2.webp) 6. Click **Save** to save the element settings. 7. Complete the journey setup, ending each branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection. When you’re ready, click **Launch journey** to activate it. ## Send Viber messages via API Send Viber messages using the [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) method in [Messaging API v2](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/). [Learn more ](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#viber-viber/) --- # Getting started with Wallet passes **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/ Design, distribute, and update Apple Wallet and Google Wallet passes in Pushwoosh, one at a time or at scale with campaigns. --- Wallet passes are digital cards, such as loyalty cards, coupons, event tickets, and boarding passes, that users keep in Apple Wallet on iPhone and Apple Watch, or in the Google Wallet app on Android. With Pushwoosh, you design a pass visually in the Control Panel, send it to users, and update it at any time without asking them to add the pass again. ## What you can do with Wallet passes * Design passes in the [Apple Wallet](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) or [Google Wallet](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) pass builder, without writing pass files by hand. * Share a pass by install link or QR code, and on Apple Wallet also as a `.pkpass` file. * Update pass content and deliver the change to every device that added the pass. * Issue one pass design to many users and update shared fields across every issued pass from one place with [Wallet Pass Campaigns](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/). * Issue a pass and send its link automatically from a Customer Journey with the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element. * Reach pass holders with a one-time push, pass update, or Customer Journey. See [Message Wallet pass holders](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/). ## Single passes or a campaign | | Single pass | Wallet Pass Campaign | | :---------------------- | :------------------------------------------------------------------ | :-------------------------------------------------------------------- | | **Best for** | A handful of recipients, or one shared pass such as a common coupon | One pass design issued to many users | | **Where you design it** | Pass builder | **Campaigns** tab in **Campaigns → Wallet Passes** | | **Updating a field** | Edit each pass | Edit the campaign once, and shared fields update on every issued pass | ## Prerequisites Developer assistance needed Issuing Wallet passes requires a developer account with Apple or Google and a one-time setup in Pushwoosh. Share this section with your developer team. **Apple Wallet:** * **Apple Developer Program:** an active membership to register Pass Type IDs and create signing certificates. [Enroll or manage your account](https://developer.apple.com/programs/) **Google Wallet:** * A [Google Pay & Wallet Console](https://pay.google.com/business/console) issuer account ## Start working with Wallet passes After the [prerequisites](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/#prerequisites) are in place, work through this flow to issue a single pass. To issue one design to many users, see [Start working with Wallet Pass Campaigns](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/#start-working-with-wallet-pass-campaigns). 1. **Configure Wallet passes in Pushwoosh** Your developer team connects the wallet platform to your application in Pushwoosh. This is a one-time setup per application: * **Apple Wallet:** create a pass signing certificate in Apple Developer, then upload it in **Configure platforms** with **SET UP PASSKIT** in the iOS row. [How to set up the signing certificate](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) * **Google Wallet:** invite the Pushwoosh service account to your issuer and add the Issuer ID. [How to set up Google Wallet](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) You can upload the certificate even if iOS push isn’t set up for the application: Apple Wallet doesn’t need iOS push notifications. You can also connect Apple Wallet yourself in [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/), without developer help. You need the **Admin** or **Account Holder** role in your Apple Developer account. Then: * Ask AI Assistant, for example, `Set up Apple Wallet for my app`. It gives you a certificate signing request file. * Follow its steps to register a Pass Type ID and create the certificate in Apple Developer. * Drop the `.cer` file from Apple into the chat. AI Assistant saves it, and you don’t need a Mac, Keychain Access, or a `.p12` file. After the setup, AI Assistant helps you design the first pass and issue it as a single pass or through a Wallet Pass Campaign. When the certificate expires in 30 days or less, it warns you at the start of a Wallet conversation and walks you through renewing it. 2. **Design the pass** In the pass builder, choose a pass style or type, add your branding and fields, and check the live preview: [Apple Wallet pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) or [Google Wallet pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/). To issue one design to many users, [create a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/) instead. 3. **Generate the pass** When the pass looks right, click **Generate**. For Apple Wallet, Pushwoosh validates the pass and prepares images at the required resolutions. For Google Wallet, Pushwoosh creates the pass in Google Wallet and returns a save link. 4. **Send the pass to users** Share the install link or QR code, or on Apple Wallet, the `.pkpass` file. See [Share the pass with users](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/#share-the-pass-with-users-via-pushwoosh-channels). When users add the pass, their device registers for future updates. 5. **Update the pass** Edit the pass in **All passes** or through the API and save. Pushwoosh delivers the update to every device that added the pass, and users don’t need to add it again. See [View and manage existing passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). 6. **Message pass holders** Once users add the pass, you can reach them with a one-time push, a pass update, or an automated Customer Journey. [Message Wallet pass holders](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/) ## Start working with Wallet Pass Campaigns A campaign needs the same one-time setup as a single pass. Then work through this flow: 1. **Create the campaign** Design the pass once, mark each field as shared or personal, and click **Create campaign**. [Create a Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/) 2. **Issue passes** On the campaign’s **Passes** tab, issue a pass to each user by User ID, or attach passes you already created. [Issue passes from a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/) 3. **Send the passes to users** Share each pass’s install link or QR code. To issue passes and send their links automatically, use the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element in a Customer Journey. [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/) 4. **Update every pass at once** Edit a shared field in the campaign and confirm the update. It reaches the issued passes. To refresh fields automatically when a user’s tag changes, [sync pass fields from tags](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/). [Manage a Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/) --- # Manage Wallet passes **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/ Edit, delete, and track Apple Wallet and Google Wallet passes from the All passes list, use the API, and find pass holders. --- After you generate a pass, you manage it from the **All passes** list: edit it, get its install link or QR code, and remove it when it’s no longer needed. Pass holders appear in User Explorer, so you can check who added the pass. To send a pass to users, see [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/). ## View and manage existing passes ### Apple Wallet 1. In the Control Panel, go to **Campaigns → Wallet Passes**. 2. Open the **All passes** tab and select **Apple Wallet**. 3. To find a pass, search by User ID or serial number, or filter the list by campaign. For every pass of the application, the table shows: * The pass name and style * The [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) it is associated with * Whether it comes from a [campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/) * The date and time it was last updated ![All passes tab with Apple Wallet selected and the actions menu (⋮) open on a pass row, showing Edit, Download, Send message, QR code, Copy install link, and Delete](https://docs.pushwoosh.com/manage-passes-1.webp) On the passes list, open the actions menu (⋮) to: * **Edit:** update the pass name and description, field values and labels, change messages, colors, barcode message, expiration and status, locations, and beacons. Pass style and images stay locked after creation. When you click **Save changes**, every device that added the pass receives the update, and users don’t need to add the pass again. To alert holders about a change, see [Apple Wallet change message](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#apple-wallet-change-message). * **Download:** download the signed `.pkpass` file. * **Send message:** open the one-time push composer to message the pass holder by [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). This option needs permission to send messages in the application and is unavailable when the pass has no User ID. See [Message Wallet pass holders](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/) for all the ways to reach them. * **QR code:** show an install QR code users can scan to add the pass to Wallet and register their device for updates. In this window, you can also use **Copy install link** below the QR. * **Copy install link:** copy the install URL to share the pass in your messaging channels. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/#share-the-pass-with-users-via-pushwoosh-channels). * **Delete:** remove the pass. Copies already installed on devices stop receiving updates and become invalid on their next refresh. ### Google Wallet 1. In the Control Panel, go to **Campaigns → Wallet Passes**. 2. Open the **All passes** tab and select **Google Wallet**. For every pass of the application, the table shows: * The pass name and style * The [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) it is linked to * The pass state * When it was last updated ![All passes tab with Google Wallet selected and the actions menu (⋮) open on a pass row, showing Edit, Send message, QR code, Copy install link, Invalidate, and Delete](https://docs.pushwoosh.com/manage-passes-2.webp) On the passes list, open the actions menu (⋮) to: * **Edit:** change field values and labels, colors, the logo and hero image, barcode, links, the app link, locations, and expiration. The pass type stays locked after creation. **Who can save the pass** shows on the edit page, but a changed value is rejected when you save. When you save, every device that saved the pass receives the update. * **Notify about changed fields:** on the edit page of loyalty, event ticket, and flight passes, this checkbox is selected by default. When it’s selected, Google sends holders an Android notification. Google writes the text from the changed fields. For notification limits, see [Google Wallet notification](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#google-wallet-notification). * **Send message:** open the one-time push composer to message the pass holder by User ID. Like on Apple Wallet, it needs permission to send messages and a User ID on the pass. See [Message Wallet pass holders](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/) for all the ways to reach them. * **QR code:** show a QR code that encodes the pass save link. Users scan it to save the pass to Google Wallet and register their device for updates. * **Copy install link:** copy the pass save link to share in your messaging channels. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/#share-the-pass-with-users-via-pushwoosh-channels). * **Invalidate:** set an active pass to inactive right away, with no confirmation. The pass moves to the user’s **Expired passes** section in Google Wallet. The record stays in Pushwoosh. * **Activate:** set an inactive pass back to active. This option appears in the actions menu only when the pass is inactive. The pass is restored in Google Wallet. * **Delete:** invalidate the pass in Google and remove its record in Pushwoosh. Google does not allow force-removing a pass already saved on a device. It becomes invalid and moves to **Expired passes**. ## Alert holders about a pass update Editing a pass and saving pushes the new version to every device that added it, without a separate message. The way you alert holders about the change differs by platform. ### Apple Wallet change message Set **Change message (update notification)** on any field you want holders alerted about. iOS shows it as a lock-screen message. Fields without a change message update silently. 1. In **Campaigns → Wallet Passes**, open **All passes → Apple Wallet**, find the pass and click **Edit** in the actions menu. 2. Change the fields you need, and add a change message to any field you want holders to be alerted about. 3. Click **Save changes**. ### Google Wallet notification For loyalty, event ticket, and flight passes, enable **Notify about changed fields** to have Google send an Android notification alongside the sync. Other pass types sync silently. 1. In **Campaigns → Wallet Passes**, open **All passes → Google Wallet**, find the pass and click **Edit** in the actions menu. 2. Change the fields you need. 3. Make sure the **Notify about changed fields** checkbox is selected. 4. Click **Save changes**. Google’s notifications have these limits: * Google generates the notification text. You can’t customize it like an Apple Wallet change message. * For event and flight passes, the notification only fires if fewer than 3 hours remain until the start time. * Google allows at most 3 notifications per pass per 24 hours. If the limit is reached, the pass updates silently. ## Manage passes via API Everything the pass builder does is also available programmatically: create, update, list, and delete passes. The Apple Wallet API also downloads the `.pkpass` file. The Google Wallet API also returns a save link and sets the pass state. To update one pass and notify its holders, call **Update a pass** ([Apple Wallet](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/#update-a-pass), [Google Wallet](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/#update-a-pass)). It updates the pass and notifies devices. On Google Wallet, set `notifyMessage` to send a custom notification alongside the update. Without it, the pass syncs silently, unless `notifyOnUpdate` is set, in which case Google sends its own notification about the changed fields for loyalty, event ticket, and flight passes. [Apple Wallet PassKit API ](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) [Google Wallet API ](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/) ## View pass holders in User Explorer When a user adds your pass, Pushwoosh lists it on the **Devices and platforms** tab in [User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). 1. Go to **Audience → User Explorer** and open the user profile. 2. Open the **Devices and platforms** tab. 3. Select **Apple Wallet** or **Google Wallet** in the sidebar. On the platform profile, you can review: * **Subscription status:** for example, **Subscribed** * **Platform identifier:** the pass identifier shown under the platform name * **Metadata:** **First subscription**, **Timezone**, and other fields when available * **Tags:** platform tags, including any you set when issuing the pass [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#devices-and-platforms-tab) --- # Message Wallet pass holders **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/message-pass-holders/ Reach Apple Wallet and Google Wallet pass holders with a one-time push, a Customer Journey, or an API call for one pass. --- Once a user adds your pass, it appears in their profile as an Apple Wallet or Google Wallet subscription tied to that [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). You can then reach the holder in these ways: * **One-time push:** send a standalone message to one holder or a wider audience. * **Customer Journey:** automate messages based on events or segments. * **API:** update one pass and notify its holders. See [Manage passes via API](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#manage-passes-via-api). To alert holders when you edit a pass, see [Alert holders about a pass update](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#alert-holders-about-a-pass-update). ## Send a one-time push to pass holders Send a standalone push to one pass holder or to a wider audience. It appears as a lock-screen message on an Apple Wallet pass, or a notification on a Google Wallet pass. 1. Open the one-time push composer. Either click **Send message** in the pass’s actions menu in **Campaigns → Wallet Passes → All passes**, or go to **Campaigns → One-time messages** and click **Send message → One-time push**. 2. On the **Content** step, write the message or select a preset. 3. On the **Audience** step, set who receives the message. The default depends on where you opened the composer: * **From Send message on a pass:** **Send to specific users** is selected, and **Recipients (user IDs)** already lists that pass holder’s [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). Send as is to reach that one holder, or change the audience: * Add more User IDs in **Recipients (user IDs)** * Switch to **Send to segment** * Choose **Send to all subscribers** * **From One-time messages:** Set the audience as in a regular [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#select-audience-for-push-notification): **Send to specific users**, **Send to segment**, or **Send to all subscribers**. ![Audience step with Send to specific users selected and the pass holder User ID pre-filled in Recipients](https://docs.pushwoosh.com/apple-wallet-passes-message-pass-holders-1.webp) 4. Under **Target platforms**, select **Apple Wallet**, **Google Wallet**, or both. 5. Send or schedule the message. Note When you target a segment, delivery goes to users in that segment who have the matching platform registered, not to everyone who saved a specific pass. ## Automate messages from a Customer Journey Send messages automatically based on events or segments. For a step-by-step example, see [How to notify wallet pass holders about a reward](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-notify-wallet-pass-holders-about-a-reward/). ### Trigger-based entry journey Developer assistance needed Sending custom events requires your developer team to integrate the [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) API call into your backend or CRM. The `event` name in the request must match an event created in your Control Panel. Share this section with them. 1. Create a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/) and add a [**Trigger-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). 2. Select the event that should start the Journey. Create a custom event in the Control Panel first if you haven’t already, for example `RewardAvailable`. 3. Add and configure a push element. Turn on [**Send to all user devices (from UserID)**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#send-by-user-id) so the push reaches every device tied to the user, including the saved pass. 4. Finish configuring and activate the Journey. When the entry event fires, Pushwoosh sends the push to every device tied to the user. Pass holders receive it as a lock-screen message on Apple Wallet, or a notification on Google Wallet. Users without a wallet pass receive it on their other devices. Pass content The push doesn’t change the fields of your pass design. On Apple Wallet, the push title shows in place of the pass’s organization name until Pushwoosh removes the message, about an hour later. To update pass content alongside the message, for example a points balance, update the pass separately: * **Apple Wallet:** via **Edit** in [All passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes), or through the [Apple PassKit API](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/). * **Google Wallet:** via **Edit** in [All passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes), or through the [Google Wallet API](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/). ### Other ways to build the journey The trigger-based flow above is one option. Depending on what should start the journey, choose a different [entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/): * **[Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/):** start the journey for a whole segment instead of a single event. Run it once, on a schedule, or periodically (for example, every week to everyone holding an active pass), with new users entering as they join the segment. * **[API-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/):** start the journey from something that happens outside the app, such as a changed event start time. Your external system sends a request to the entry element and uses a filter to pick the users, for example everyone with the tag `eventId = 1234` set when the pass was issued. A good fit for event tickets and boarding passes. --- # Share Wallet passes **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/ Send an Apple Wallet or Google Wallet pass install link by email, push, or SMS, and see how one Apple link maps to one pass. --- A user adds a pass to Apple Wallet or Google Wallet by opening its install link or scanning its QR code. Copy the link or show the QR code from **All passes**, then send it in the channels you already use. ## Get the install link or QR code 1. Go to **Campaigns → Wallet Passes**, open the **All passes** tab, and select **Apple Wallet** or **Google Wallet**. 2. Find the pass and open its actions menu (⋮). 3. Click **Copy install link**, or click **QR code** to show a code users can scan. For all actions in this menu, see [Manage Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). ## What the install link does The install link is a regular URL: * **Apple Wallet:** opening it on an iPhone adds the pass to Wallet. * **Google Wallet:** it’s an **Add to Google Wallet** link (`https://pay.google.com/gp/v/save/...`) that prompts the user to save the pass. **Who can save the pass**, a setting you choose when you create the pass, decides who the link is for. See [Who can save the pass](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#who-can-save-the-pass). ## Share the pass with users via Pushwoosh channels Use the link in the same Pushwoosh channels you already use to reach your audience: * **Email:** add the link to a button or text link in the [email editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/), for example, an “Add to Apple Wallet” or “Add to Google Wallet” call to action. * **Push notifications:** set the link as the notification’s [open action](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#define-what-happens-when-the-push-is-clicked) so a tap opens the pass. * **SMS:** include the link in [SMS content](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-sms-via-customer-journey/#configure-sms-content). One Apple Wallet link works for one pass * Each Apple Wallet install link carries one pass’s serial number and token, so one link fits one shared pass, such as a common coupon or event ticket. * To give every recipient their own pass, generate a pass per user with the [PassKit API](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) or issue it from a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/), and send each user their own link. * Apple Wallet passes install on iPhone and Apple Watch only. --- # About Wallet Pass Campaigns **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/ Design an Apple or Google Wallet pass template, issue passes from it, and push shared field updates to every device at once. --- A Wallet Pass Campaign lets you design a single Apple Wallet or Google Wallet pass template, issue it to multiple users, and centrally manage updates. You choose which fields are [**Shared**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/#set-shared-and-personal-fields) (syncing changes to all user devices simultaneously) and which are [**Personal**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/#set-shared-and-personal-fields) (preserving unique values like names, barcodes, or balances for each user). Note A Wallet Pass Campaign is unrelated to a Customer Journey campaign or a push notification. It is a tool for managing pass templates, issuance, and real-time pass updates. ## What you can do with Wallet Pass Campaigns * Design one pass in a campaign, instead of setting up each pass by hand in the [Apple Wallet](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) or [Google Wallet](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/) pass builder. * Choose which fields update everywhere (shared) and which stay per pass (personal). * See how many issued passes a field edit affects before you save it. * Override a shared field on one pass and, on Apple Wallet passes, hand control back to the campaign later. * Attach passes issued outside a campaign so they start following its shared fields. * Track campaign revisions and the progress of an update while it’s delivered. * Automatically refresh a pass whenever the tag behind one of its fields changes, without editing the campaign. ## Prerequisites A campaign uses the same one-time setup as a standalone pass: **Apple Wallet:** * Active [Apple Developer Program](https://developer.apple.com/programs/) membership * [Pass signing certificate](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/) uploaded to Pushwoosh **Google Wallet:** * Active [Google Pay & Wallet Console](https://pay.google.com/business/console) issuer account * [Google Wallet configured](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/) for the application (Issuer ID, with the Pushwoosh service account invited to your issuer) ## Work with a campaign * [Create a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/): choose the platform, design the pass, and mark fields as shared or personal. * [Sync pass fields from tags](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/): refresh passes automatically when a tag behind a field changes. * [Issue passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/): issue passes to users, attach existing passes, and deliver them. * [Manage a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/): change status, push design updates, and track issued passes and revisions. --- # Create a Wallet Pass Campaign **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/ Set up the pass template for an Apple Wallet or Google Wallet campaign, with design, shared and personal fields, and images. --- A campaign holds one pass design that you issue to many users. You set it up once on the **New campaign** page, and every pass you issue later follows it. To start, go to **Campaigns → Wallet Passes**. On the **Campaigns** tab, click **Create campaign**. ![Empty Campaigns list in Wallet Passes with the Create campaign button highlighted](https://docs.pushwoosh.com/wallet-pass-campaigns-1.webp) ## Set campaign details and platform 1. At the top of the page, select the **Apple Wallet** or **Google Wallet** tab. 2. Optionally, click **Load template** to start from a built-in pass instead of an empty one. 3. Under **Campaign**, enter a **Name** and an optional **Description** for your internal reference. Neither appears on the pass. 4. Under **Pass Style** (Apple) or **Pass Type** (Google), choose the layout for your pass: * **Apple Wallet:** Boarding Pass, Coupon, Event Ticket, Store Card, Generic. See [Choose a pass style](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#choose-a-pass-style). * **Google Wallet:** Generic, Offer, Loyalty card, Event ticket, Gift card, Flight, Transit. See [Choose a pass type](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#choose-a-pass-type). You can’t change the platform or the pass style or type after you create the campaign. ![New campaign creation form showing Name and Description fields, and a Pass Style picker](https://docs.pushwoosh.com/wallet-pass-campaigns-2.webp) ## Configure pass design and branding Configure the standard sections of the pass: * **Basic Information, Colors (Appearance on Google Wallet), and Barcode:** set the pass title, background and text colors, and barcode type. You personalize the barcode in the **Barcode** section too (see [Apple Wallet barcode](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#add-a-barcode) or [Google Wallet barcode](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#add-a-barcode)). The campaign uses the same design sections as the pass builder, so for detailed guidance on each section, see the [Apple Wallet pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) or the [Google Wallet pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/). * **Pass fields:** add the fields that should appear on the card (see [Apple Wallet pass fields](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#fill-in-pass-fields) or [Google Wallet fields and details](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#fill-in-pass-fields)). * **Expiration:** set relevant dates and expiration rules (see [Apple Wallet expiration](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#set-expiration-and-status) or [Google Wallet expiration](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#expiration)). * **Locations:** set location-based relevance (see [Apple Wallet locations](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#location-based-relevance-optional) or [Google Wallet locations](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#add-locations-optional)). * **Who can save the pass (Google Wallet only):** choose whether the pass is tied to a single Google account, a single device, or accessible to anyone with the link. ## Set shared and personal fields In the **Shared and personal fields** section, configure which fields update globally across all passes and which keep unique values per user. This controls what happens when you edit the campaign later: shared fields update on every issued pass, while personal fields remain unchanged. Turn on the **Personal** toggle for any field that should hold unique values per user. A field whose value is a single tag placeholder, such as `{Seat}`, is personal by default. ![Shared and personal fields configuration section with Personal toggles next to each field](https://docs.pushwoosh.com/wallet-pass-campaigns-3.webp) | Field type | Behavior | Examples | | :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | :---------------------------------------------------------- | | **Shared** | Driven by the campaign. Updating this field syncs the new value to every pass on users’ devices, except passes where you changed this field by hand. See [Override a shared field on an individual pass](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#override-a-shared-field-on-an-individual-pass). | Event date, Gate number, Store address, Discount percentage | | **Personal** | Unique per user. The campaign does not overwrite these values when updated. | User name, Seat number, Membership ID, Points balance | A field’s value can include a tag placeholder, such as `{Seat}`. Pushwoosh fills it in from the recipient’s [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) when it serves the pass, in shared and personal fields alike. * **Insert a placeholder:** click `{ }` next to the field. * **Add a format and a fallback:** write `{tag|format|fallback}`. If the recipient has no value for the tag, Pushwoosh uses the fallback, or leaves the field blank when the placeholder has none. How a **Personal** field appears in the **Issue pass** dialog depends on its value. Shared fields never appear there. * **Just the tag, nothing else, like `{Seat}`:** the field doesn’t appear in the **Issue pass** dialog. * **The tag mixed with other text, like `Seat {Seat}`, or no tag at all:** the field appears as an editable input in the **Issue pass** dialog, prefilled with that value. Any tag inside it still resolves per recipient when Pushwoosh serves the pass. ## Upload campaign images 1. In the **Campaign images** section, click **Choose an image** next to an image slot. 2. Pick an image in one of these ways: * Select an existing image from your [Media storage](https://docs.pushwoosh.com/product/content/media-store/). * Drag and drop a new PNG or JPEG file to upload it. * Get one in the **Stock photos** or **Generate with AI** tab. 3. Repeat for each available image slot: * **Apple Wallet:** Icon, Logo, Background, Footer, Strip, and Thumbnail (see [Apple Wallet images](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#images)). **Icon** is required to create the campaign. * **Google Wallet:** Logo and Hero image. For the Apple Wallet **Logo** and **Strip** slots, Pushwoosh crops an image wider than 3:1 to 3:1 automatically, keeping its full height, and uses the crop instead of the original. The crop is saved in Media storage next to the original. If the crop can’t be created, the original image is used. ![Campaign images section with upload slots for icons, logos, and custom pass imagery](https://docs.pushwoosh.com/wallet-pass-campaigns-4.webp) Images must be under 10 MB. Pushwoosh scales down very large images and rejects images over about 50 megapixels. You can replace a campaign image at any time, and one edit reaches every issued pass: * **Apple Wallet:** images of a standalone pass are locked after creation, but campaign images aren’t. * **Google Wallet:** on a standalone pass, you change the logo and hero image one pass at a time via **Edit**. ## Turn on tag sync *(optional)* To refresh issued passes automatically when a tag behind one of their fields changes, turn on **Update pass fields from tags** in the **Tag sync** section at the top of the page. Tag sync runs only while the campaign is **Active**, and a new campaign is created as **Draft**, so set its status to **Active** after you create it. See [Sync pass fields from tags](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/). ## Preview and create the campaign 1. Check the preview panel on the right to see how the card looks. For Apple Wallet campaigns, enter a User ID in **Preview as user** to preview personalized fields. ![Live pass preview panel with Preview as user field and the rendered digital pass card](https://docs.pushwoosh.com/wallet-pass-campaigns-6.webp) 2. Optionally, click **Validate pass** to check the design against Apple’s or Google’s requirements. This doesn’t create the campaign. 3. Click **Create campaign** at the bottom of the page. Pushwoosh creates the campaign in **Draft** status and opens its workspace, with **Design**, **Passes**, and **History** tabs. Creating a campaign only saves the pass design and doesn’t send passes to users. To distribute passes, go to [Issue passes from a campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/). --- # Issue passes from a campaign **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/ Issue Wallet passes to users from a campaign, attach existing standalone passes, and deliver each pass to its recipient. --- Issue a pass from a campaign to give a user their own copy of the campaign’s design, or attach passes you created earlier so they follow the campaign too. You can’t issue or attach passes while the campaign is **Archived**. ## Issue a pass from the campaign 1. On the **Passes** tab, click **Issue pass**. 2. Enter the recipient’s Pushwoosh [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). ![Issue a pass from this campaign dialog with User ID set to u12345, the seat field set to B-07, and Issue pass](https://docs.pushwoosh.com/wallet-pass-campaigns-8.webp) 3. Click **Issue pass** to confirm. Depending on how you configured **Personal fields**, the dialog handles them as follows: * **Editable fields:** appear under their field key, with default values that you can change for this user. * **Tag placeholders (like `{Seat}`):** do not appear in the dialog. Pushwoosh automatically populates them from the user’s tags. You can also edit any personal field on an individual pass at any time after issuance. Pushwoosh generates the pass from the campaign’s current design and lists it on the [**Passes** tab](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#passes-tab) and in the application’s [**All passes**](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes) table. ## Deliver the pass to the recipient Issuing a pass creates the pass record in Pushwoosh. To deliver it to the user’s device, share the install link: 1. Open the pass’s actions menu on the **Passes** tab or in **All passes**. 2. Select **Copy install link** or **QR code**. Apple Wallet passes also offer **Download** for the signed `.pkpass` file. 3. Send the link or file to the recipient. Other ways to deliver passes: * **Share in your channels:** send the link by email, push, or SMS. See [Share Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/share-passes/#share-the-pass-with-users-via-pushwoosh-channels). * **Issue and deliver automatically:** use the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element in a Customer Journey. It issues a pass as users move through the journey, and a later Push or Email element sends them the link, so you don’t issue passes here. ## Attach an existing pass to a campaign You can attach standalone passes (created outside any campaign) to a campaign so they start receiving shared updates. 1. On the **Passes** tab, click **Attach passes**. The list shows only passes that don’t belong to any campaign. ![Attach existing passes dialog with a search field, a list of passes outside any campaign, two passes selected, and the Attach button](https://docs.pushwoosh.com/wallet-pass-campaigns-22.webp) 2. Search for passes by User ID, serial number, or barcode. 3. Select the passes and click **Attach N**, where N is the number of selected passes. A pass that can’t be attached stays in the list with the reason next to it: * The pass already belongs to another campaign. * The pass style differs from the campaign’s. * The pass has no field for one of the campaign’s fields. Once attached: * **Shared fields** with the same value as the campaign start following it and update whenever you change the pass template. * **Shared fields** with a different value keep it and become [overrides](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#override-a-shared-field-on-an-individual-pass) on that pass. * **Personal values** already on the pass (such as user details or unique balances) are preserved. --- # Manage a Wallet Pass Campaign **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/ Change campaign status, push design updates, override shared fields, track issued passes and revisions, and delete a campaign. --- After you create a campaign, you manage it from its own page: change the design and push it to issued passes, track every pass the campaign issued, and review past revisions. To open it, go to **Campaigns → Wallet Passes** and click the campaign on the **Campaigns** tab. The page has three tabs: **Design**, **Passes**, and **History**. ![Campaign page with Design, Passes, and History tabs, and the Design tab showing the revision counter and a Status dropdown set to Draft](https://docs.pushwoosh.com/wallet-pass-campaigns-11.webp) ## Change the campaign status Use status to track where each campaign stands: still being designed, actively issuing passes, or done and archived. To change the status, select an option from the **Status** dropdown at the top of the **Design** tab. The change saves immediately without a separate confirmation step and counts as a new revision: * **Draft** or **Active:** you can issue new passes, attach existing ones, and push design updates. [Tag sync](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/#when-tag-sync-runs) runs only in **Active**. * **Archived:** you can’t issue or attach passes. Previously issued passes keep working and receiving shared updates. ## Design tab On the **Design** tab, you can update the pass template, change colors, modify shared and personal fields, upload new campaign images, or change [tag sync settings](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/). ### Update design and push changes to passes 1. On the **Design** tab, edit basic information, colors, barcode type, expiration, locations, a shared field, or a campaign image. 2. Click **Save changes**. 3. In the **Apply the campaign edit** dialog, confirm the update: * **No pass overrides the changed field:** click **Update N, keep manual edits** to push the edit to all N passes. * **Some passes override the changed field:** the dialog names the field and how many passes override it. Click **Update N, keep manual edits** to push the edit to every pass except those, or **Overwrite all N** to push it to all N passes, including the overridden ones. Overwriting erases the override. * **Every affected pass overrides the changed field:** only **Overwrite all N** is available. ![Apply the campaign edit dialog listing the venue field overridden on 1 pass, with Update 3, keep manual edits and Overwrite all 4 buttons](https://docs.pushwoosh.com/wallet-pass-campaigns-16.webp) Whichever button you click, the update saves as a new revision (for example, **Revision 2**), so you can check when it changed on the [**History** tab](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#history-tab). ![History tab with the revision 2 row highlighted and its creation timestamp](https://docs.pushwoosh.com/wallet-pass-campaigns-15.webp) Note An edit that doesn’t change any issued pass saves immediately without a confirmation dialog. This happens when no passes are issued yet, or when you change only the campaign name, description, or tag sync settings. While an update is being delivered to issued passes, an **Update in progress** panel shows its progress below the tabs on every tab of the campaign page. To stop the update, click **Cancel the run**. Passes it has not reached yet stay on their previous revision. Pushwoosh marks a pass as failed after 5 unsuccessful delivery attempts. A failed pass stays on its previous revision, so it appears under the **Behind the revision** filter on the [**Passes** tab](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#passes-tab). ### Override a shared field on an individual pass A field changed by hand on one pass is called an override. The **Pinned fields** column on the **Passes** tab lists the overrides of each pass. If you need to customize a shared field for a specific user, edit that field directly on their individual pass (from the **Passes** tab or **All passes**): * **Create an override:** saving a custom value on an individual pass detaches that field from campaign-wide updates. Future updates to the campaign, including a scheduled [tag sync](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/) refresh, will skip this pass for that specific field. * **Remove an override:** on an Apple Wallet pass, edit the pass and click **Use the campaign value** next to the overridden field to restore campaign control. Google Wallet passes have no such button, so an override on a Google Wallet pass stays until you click **Overwrite all N** in a campaign edit. ![Edit pass screen showing a From campaign field with Use the campaign value button highlighted](https://docs.pushwoosh.com/wallet-pass-campaigns-13.webp) To see which passes have overrides, open the [**Passes** tab](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/#passes-tab) and check the **Pinned fields** column. ## Passes tab Use the **Passes** tab to check every pass the campaign issued. For each pass, the table shows the serial number, recipient User ID, active pinned fields, current revision, and last update date. ![Passes tab showing issued passes table with User ID, pinned fields, revision, and action buttons](https://docs.pushwoosh.com/wallet-pass-campaigns-7.webp) If [tag sync](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/) is on, the tab also shows a **Tags synced** line above the table with the date and time of the last scheduled run, or **not yet** before the first one. Use table filters to find specific passes: * **With pinned fields:** shows passes that have at least one override. * **Behind the revision:** shows passes that are not on the campaign’s latest revision. A change of status, name, description, or tag sync settings also creates a revision without updating any pass, so after such a change every pass shows as behind. To issue new passes or attach existing ones, see [Issue passes to users](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/). Open a row’s actions menu (⋮) to manage that pass: * **Edit:** open the pass on its platform’s edit page to change personal fields or override a shared one. * **Download**, **Send message**, **QR code**, and **Copy install link:** work as they do in **All passes**. See [View and manage existing passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes). **Download** appears on Apple Wallet campaigns only, because Google Wallet has no downloadable pass file. * **Delete:** remove the pass. This can’t be undone. On Apple Wallet, already-installed copies stop receiving updates and become invalid on their next refresh. On Google Wallet, Google doesn’t allow force-removing a pass already saved on a device, so it becomes invalid and moves to the user’s **Expired passes** while Pushwoosh removes its record. Deleting a pass here doesn’t delete the campaign or any other issued pass. ## History tab The **History** tab lists all revisions of the campaign, newest first. For each revision, it shows the revision number and when the revision was created. ![History tab showing the list of campaign revisions with their numbers and creation times](https://docs.pushwoosh.com/wallet-pass-campaigns-10.webp) ## Delete a campaign Open the campaign, click **Delete campaign** at the top right of the page, and confirm in the **Delete this campaign?** dialog. ![Campaign workspace header with the Delete campaign button highlighted at the top right](https://docs.pushwoosh.com/wallet-pass-campaigns-12.webp) Deleting a campaign does not delete or invalidate already issued passes. Issued passes retain their current content and images, but stop receiving shared updates. Any future changes must be made on each pass individually. --- # Sync pass fields from tags **URL:** https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/ Refresh issued Wallet passes automatically when the tag behind one of their fields changes, and choose how holders are notified. --- Tag sync keeps issued passes up to date with your users’ [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). When a tag behind a field’s placeholder changes, Pushwoosh sends the new value to the pass, so you don’t need to edit the campaign by hand. For example, a loyalty card has a field with the value `Points {Points}`. When a customer’s **Points** tag changes from 120 to 150, the pass on their phone shows 150 without you editing the campaign. ## Turn on tag sync In the **Tag sync** section of a new campaign or on the campaign’s **Design** tab, turn on **Update pass fields from tags**. Pushwoosh then re-checks the [tag placeholders](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/#set-shared-and-personal-fields) in the campaign’s fields on a schedule and sends a fresh value to every pass whose tag changed. * **Interval, minutes:** how often Pushwoosh re-reads the tags. Defaults to 60 and can’t go below 5. A pass gets at most one refresh per interval, no matter how many times its tag changes in between. * **Notification:** how the holder learns about the refresh. On Google Wallet, the same setting also applies when you push a design update from the campaign, even after you turn tag sync off. * **Google Wallet:** choose **Silently**, **Custom message** (fill **Message text**, sent with the update), or **Google field notification** (Google’s own field-update alert). Google caps notifications at 3 per pass per 24 hours. Past that, the new values still reach the pass, just without a notification. ![Tag sync section for a Google Wallet campaign with Notification set to Custom message and the Message text field filled in](https://docs.pushwoosh.com/wallet-pass-campaigns-20.webp) * **Apple Wallet:** there is no campaign-wide notification, so this picker doesn’t appear for Apple campaigns. The notification text lives on the field itself: fill **Change message** on each field in the [Apple Wallet pass builder](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#fill-in-pass-fields). ![Tag sync section for an Apple Wallet campaign with the interval field and a note about Change message instead of a notification picker](https://docs.pushwoosh.com/wallet-pass-campaigns-21.webp) ## When tag sync runs Tag sync only runs while the campaign’s status is **Active**. Set it to **Draft** or **Archived** to pause scheduled checks without turning the toggle off. After the first run, the **Tag sync** section on the **Design** tab shows **Last tag sync** with the date and time of the latest check. Caution If a campaign already has issued passes, the first refresh runs at the next check and doesn’t wait for the interval to pass. In **Custom message** or **Google field notification** mode, this can notify every holder at once and use up the limit of 3 notifications per pass per 24 hours in a single run. --- # Understanding Web popups **URL:** https://docs.pushwoosh.com/product/messaging-channels/web-popups/ Web campaigns show your web content on site pages as a popup or an inline block. Learn the setup steps and how visits are counted. --- Web campaigns show your web content to visitors while they browse your site. A **Popup** campaign appears over the page, and an **Inline** campaign appears as a block inside an element of the page. Use them for promotions, onboarding hints, email signups, and other goals. ## When to use web campaigns Common use cases: * **Promotions and sales:** discounts, coupons, or seasonal offers on browsing or category URLs where the message fits what the visitor is doing on that page. * **Welcome or first-purchase offers:** **New visitors** (or a broad audience) on the home page, signup flow, or a dedicated landing path. * **Product or feature announcements:** draw attention on the sections of the site where the news is relevant (for example a product area or changelog page). * **Lead capture:** point to a form, demo, or download on pages that already match the offer (pricing, resources, contact). * **Onboarding or guidance:** short tips on specific pages you target in the campaign audience settings, such as pricing, help, or account settings. ## Start working with Web popups Before you can show web campaigns on your website, complete the following steps: 1. **Integrate the Web Push SDK** Your development team needs to add the Web Push SDK to your site. Web campaigns load by default once the SDK is initialized, so no extra setup is required to turn them on. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#initialize-the-sdk) 2. **Create web content or a web form** Open **Content → Web content**, click **Create**, and choose the type: * **Web content:** shows a message, for example an offer or an announcement. * **Web form:** collects an email address and visitor answers. You choose this content when you configure the campaign. [Learn more](https://docs.pushwoosh.com/product/content/web-popup-content/) 3. **Create a web campaign** Go to **Campaigns → Web campaigns**, click **Create campaign**, choose **Popup** or **Inline**, and configure content, audience, and display settings. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) ## View and manage campaigns Go to **Campaigns → Web campaigns** to manage all Popup and Inline campaigns in one place. Use **Create campaign** to start a new wizard. To find a campaign, search by its name or code. To narrow the list, filter it by status (**All**, **Draft**, **Active**, or **Inactive**) and by type (**All types**, **Web popups**, or **Inline blocks**). ![Web campaigns list with search, the status filter, the All types filter highlighted, and Create campaign above the table](https://docs.pushwoosh.com/messaging-channels-web-popups-index-1.webp) ### Monitor your campaigns In the table, you can review how each web campaign is performing. Check **Status** to see whether a campaign is live. The columns report the following: * **Name:** the campaign name. Below it, the campaign type (**Popup** or **Inline block**) and the campaign code. * **Visits:** how many page loads got the campaign ready to show, including repeat visits by the same visitor. A Popup counts a visit once the visitor matches its settings, before the display delay runs out. An Inline campaign counts a visit once its block is placed on the page. * **Impressions:** how many times visitors actually saw the campaign, including repeat views. A Popup counts when it’s shown, an Inline campaign when it scrolls into view. * **Interactions:** how many times visitors clicked links and buttons in the content or submitted a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/), including repeat clicks. Closing the campaign with ✕ doesn’t count. A campaign without clicks shows 0. Click a campaign name to open its [statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/). ### Take action on a campaign Click the three dots on a campaign’s row to open its actions menu: * Choose **Activate campaign…** or **Deactivate campaign…** to turn the campaign on or off, and confirm in the dialog. * Choose **View statistics** to open the campaign’s [statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/). * Choose **Edit campaign…** to change content, audience, or display settings. * Choose **Rename…** to change the campaign name only, without opening the full editor. * Choose **Duplicate campaign…** to copy settings as a starting point. * Choose **Get embed code…** on an **Inline** campaign to see the element to add to your page. * Choose **Delete campaign…** to remove the campaign. This action cannot be undone. ![Web campaigns list with the row actions menu of an Inline campaign open and Get embed code highlighted](https://docs.pushwoosh.com/messaging-channels-web-popups-index-2.webp) ## Track campaign results Open the campaign’s statistics from **Campaigns → Web campaigns** to see how many times the campaign loaded on a page and was seen. For a web form, the statistics also show submits, failed submissions, and the subscribers you can export. [Web campaign statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/)Visits, impressions, submits, failed submissions, and the subscriber export. --- # Create Web popup campaigns **URL:** https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/ Build Web popup campaigns: pick content, target an audience by segment or custom conditions, set display delay, frequency, device, and pages, then launch or save as draft on Review. --- A web campaign places the web content you’ve created onto your website and controls when it appears and who sees it. A campaign shows its content in one of two ways: as a **Popup** or as an **Inline** block. ## Start a new campaign 1. Go to **Campaigns → Web campaigns**. 2. Click **Create campaign** and choose the type: * **Popup:** an overlay on top of the page. * **Inline:** a block inside an element of your page, for example in a sidebar or under an article. You can’t change the type later. ![Web campaigns list with the Create campaign menu open, offering Popup and Inline](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-12.webp) Web SDK version for Inline campaigns Ask your developer to check the [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/#inline-campaigns-and-web-forms) version on your site. Inline campaigns need 3.97.0 or later, or 3.98.0 or later if the site uses the npm package. With an older SDK, an Inline campaign appears as a popup over the page instead of inside the block. ### Select web content Choose the content this campaign shows to visitors. You can pick existing content or make new content without leaving the wizard. * **Existing content:** in **Web content**, select an item from the list. Under each name, the list shows its type: **Web content** for regular content or **Web form** for a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). * **New content:** click **Create new** and choose **Web content** or **Web form**. The editor opens for that content type. ![Content step with a web form selected, the Create new menu open with Web form highlighted, and a preview of the form](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-13.webp) To change the order of content in the dropdown, use the sort button above it and choose **Last updated**, **A-Z**, or **Z-A**. The dropdown is sorted **A-Z** until you pick another order. Pushwoosh remembers your choice and uses it in every Web popup content dropdown, in one-time messages and journeys alike. Collect emails and answers Select a web form to subscribe visitors to email and save their answers to tags. The same form works in Popup and Inline campaigns. [Learn how to build one](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/). Click **Next** when the right content is selected. ### Define audience Select who should see the campaign. * **All users:** show the campaign to everyone without filters. ![Audience step with Target audience selected, showing Use existing segment and Use custom conditions options](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-6.webp) * **Target audience:** show the campaign only to users who match a segment or custom conditions. Two sub-options appear: * **Use existing segment:** pick a saved [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) from the list. To create one without leaving the wizard, click **Create segment**. * **Use custom conditions:** build the audience inline by adding filters by [**Tag**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/), [**Event**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/), [**Segment**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/), or [**User ID**](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). ![Use existing segment selected with the Active Users segment chosen and the Create segment button](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-8.webp) When you’re done, click **Next**. ### Configure display settings Set up when, how often, and where to show this campaign. #### CSS selector This field appears for **Inline** campaigns only. An Inline campaign appears inside a block of your page, and the CSS selector tells Pushwoosh which block. 1. In **CSS selector**, enter the selector of the page element the campaign is placed into, for example `#newsletter-signup`. Use one simple selector: a tag, an `#id`, a `.class`, or an `[attribute]`, without nesting. 2. Under the field, Pushwoosh shows the element to add to your page, for example `
`. Ask your developer to add it where the campaign should appear. ![CSS selector field set to #newsletter-signup, with the element to add to the page and the hint below](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-14.webp) The campaign appears inside the first element on the page that matches the selector. If the page has no matching element, the campaign isn’t shown there. With **Automatically on page load**, an **Inline** campaign appears each time a page with the matching element loads. #### Show popup or Show campaign Choose how the campaign is shown. The field is called **Show popup** in a **Popup** campaign and **Show campaign** in an **Inline** campaign. * **Automatically on page load:** the campaign appears by itself on pages that match its settings. * **Only via WebSDK call:** the campaign doesn’t appear by itself. Your site shows it by calling the Web SDK. The wizard shows the call with the campaign code, for example `Pushwoosh.moduleRegistry.webPopups.show('CAMPAIGN_CODE')`. Requires Web SDK 3.63.0 or later. With **Only via WebSDK call**, the wizard hides **Display delay** and **Show on pages**, because a call from your site ignores them. #### Display delay This setting is for **Popup** campaigns shown **Automatically on page load**. Choose when the Web popup may first appear after the page loads. * **Immediately** * **After 3 seconds** * **After 5 seconds** * **After 10 seconds** How the delay is measured * The delay is measured from the moment the Pushwoosh SDK initializes on the page, not from when the page starts loading. * The actual time before the Web popup appears may be slightly longer than the value you set. For example, if the SDK takes 2 seconds to load and you set a 5-second delay, the Web popup appears 7 seconds after the page starts loading. #### Frequency This setting is for **Popup** campaigns only. Under **FREQUENCY**, choose how often a visitor can see this Web popup: * **Once per visitor:** the visitor sees the popup at most once in that browser, even in a new tab. Note Pushwoosh can recognize the same visitor only while the browser keeps the Web SDK data. If the visitor clears browser data, uses another browser, or opens the site on another device, Pushwoosh treats them as a new device, so the Web popup can appear again. * **Once per session:** the visitor sees the popup once per browsing session. If they close a tab and return before the session ends, the popup does not appear again. * **Every visit:** the popup appears each time the visitor loads a matching page. ![Display settings with display delay, frequency, device, and show on pages options](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-4.webp) #### Device In **Device**, choose the devices the campaign appears on: **All devices**, **Desktop**, or **Mobile**. #### Push permission In **Push permission**, choose which visitors see the campaign, depending on whether they allowed web push notifications in this browser: * **All visitors**: everyone, whatever their push permission. This is the default. * **Not asked yet**: visitors the browser hasn’t asked for push permission yet. Use it for a prompt that invites them to subscribe. * **Subscribed**: visitors who allowed push notifications. * **Blocked**: visitors who blocked push notifications, for example to explain how to allow them again in the browser. * **Push not supported**: visitors whose browser can’t receive web push notifications. Use it to offer another channel, such as an email form. ![Display settings with Device set to All devices and Push permission set to Not asked yet](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-9.webp) The **Review** step shows the option you chose next to the device, as **Push permission:** and the option name. The visitor’s browser checks the permission when the campaign is about to show. If the visitor allows or blocks notifications on the page, campaigns that no longer match are removed, and a campaign that starts to match shows after the next page load. Web SDK version for push permission This setting needs [Pushwoosh WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/) 3.103.0 or later. If your site loads web campaigns with an older WebSDK core, only campaigns set to **All visitors** show there. #### Show on pages Choose the pages where the campaign can appear. This setting is hidden with **Only via WebSDK call**. Web SDK version for page rules Excluding pages, and the **Starts with** match type, both take effect only on sites running an up-to-date [Pushwoosh WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/). WebSDK 3.104.1 and later match paths with letter case, while 3.102.0 to 3.104.0 ignore it. On WebSDK versions earlier than 3.102.0, the paths you enter also don’t match pages whose address contains spaces or letters outside the English alphabet, such as `é` or `ñ`. ##### All pages Show the campaign on every page of the site. To keep it off some pages, add up to 10 exclusions: 1. Click **+ Add exclusion**. 2. Enter the page [path](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#enter-a-page-path). 3. Choose how the path should match: **Exact match** or **Starts with**. ![Page path row with Exact match and Starts with dropdown options](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-7.webp) ##### Specified pages Show the campaign only on the pages you list. You can add up to 10 pages: 1. Click **+ Add page**. To remove a row, click ×. 2. Enter the page [path](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#enter-a-page-path). 3. Choose how the path should match: **Exact match** or **Starts with**. ![Show on pages step with Specified pages selected and Add page rows listing included page paths](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-11.webp) Click **Next** to continue. ##### Enter a page path The path tells Pushwoosh which pages a rule covers. The same rules apply to exclusions and to specified pages. **What to enter** * Enter the path as it appears after your domain, for example `/pricing` or `/blog/intro`. * The path can contain letters of any alphabet, digits, spaces, and the characters `- _ . ~ ! $ & ' ( ) * + , ; = : @ /`. * Paths are case-sensitive, so `/Pricing` and `/pricing` are different pages. Paths saved from October 27 to November 2, 2026 Pushwoosh saved paths entered in that period in lowercase. On WebSDK 3.104.1 and later, such a rule no longer covers pages whose path has capital letters, with **Exact match** and **Starts with** alike. Open the campaign and enter the path again in your site’s letter case. **Pasting a full address** You can also paste the full address from the browser’s address bar or an encoded path. Pushwoosh removes the domain, the query string, and the fragment, then saves the path decoded, with the letter case kept. For example: | You enter | Pushwoosh saves | | ----------------------------------------- | --------------- | | `https://example.com/Catalog/?sort=price` | `/Catalog/` | | `/caf%C3%A9` | `/café` | **Match types** The match type decides which pages the path covers: | Match type | Pages it covers | | --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Exact match** | Only the page whose path is the same as what you enter. `/pricing` doesn’t cover `/pricing/` or `/Pricing`. | | **Starts with** | Every page whose path begins with what you enter, in the same letter case. `/blog` covers `/blog/intro` and also `/blog-news`, so enter `/blog/` to cover only the blog section. | ## Review the campaign The **Review** screen summarizes: * **Content:** the web content attached. See [Select web content](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#select-web-content). A campaign still linked to a legacy subscription form instead of web content shows that form’s name here instead. * **Audience:** **All users**, or **Target audience** with the selected segment name or custom conditions. * **Display settings:** device, the pages the campaign shows on (including match type and any exclusions), delay, and frequency. Use **CHANGE OPTIONS** on any block to jump back and edit that part. The **Preview** area shows how the content looks on a sample viewport. Use **EDIT CONTENT** if you need to adjust the underlying content asset. ![Review step with Content, Audience, and Display settings summary, Change options, and Preview area](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-5.webp) When everything looks right, click **Complete setup**. Then pick an option from the menu: * **Launch campaign…** activates the campaign after you enter a campaign name and confirm. For an **Inline** campaign, an **Embed code** dialog then shows the element to add to your page. You can open it again later with **Get embed code…** in the campaign’s row menu. ![Embed code dialog showing the div element with the newsletter-signup id to add to the website](https://docs.pushwoosh.com/messaging-channels-web-popups-create-web-popups-campaigns-15.webp) * **Save as draft…** saves the campaign without activating it. To go live later, go to **Campaigns → Web campaigns**, open the three-dot menu on the draft’s row, and choose **Activate campaign…**. To change settings first, choose **Edit campaign…** in the same menu. To search, filter, and manage campaigns from the list, see [View and manage campaigns](https://docs.pushwoosh.com/product/messaging-channels/web-popups/#view-and-manage-campaigns). --- # Getting started with WhatsApp messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/ Learn how to send and automate WhatsApp messages with Pushwoosh for enhanced customer communication and engagement. --- WhatsApp is the ultimate channel to communicate with customers when they ignore other channels, such as push notifications, in-app messages, or emails. ## Types of WhatsApp campaigns in Pushwoosh Pushwoosh supports both outbound and inbound-triggered WhatsApp campaigns, giving you flexibility to engage customers proactively or in response to their actions. ### Outbound campaigns These are the most common type of WhatsApp messages when you initiate the conversation with users.\ They include: * Promotional and marketing messages (discounts, product launches, reminders) * Transactional notifications (order confirmations, shipping updates) * Service or informational messages (appointment reminders, policy updates) * Verification messages (password reset, two-factor authentication (2FA)) Outbound messages are sent using [approved WhatsApp templates](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/#create-whatsapp-templates). ### Inbound-triggered campaigns You can automatically trigger WhatsApp campaigns in response to inbound messages from users. For instance, if a user sends a specific keyword like *“Help”* or *“Promo”* to your WhatsApp Business Account, an automated campaign can start. Pushwoosh automatically logs a [`PW_WhatsAppReceived` event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_whatsappreceived) whenever an inbound WhatsApp message is received. You can use this event to start a [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). This lets you automatically: * Start a support or feedback flow * Send an acknowledgment message * Deliver a follow-up offer or reply ## Benefits of sending WhatsApp messages through Pushwoosh ### Segmentation for WhatsApp users Pushwoosh enables precise audience segmentation to enhance message relevance and engagement. You can: * Use [default tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#default-tags) and [default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events) available out-of-the-box. * Leverage [custom tags and events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/) from multiple sources, including your mobile app, backend, marketing acquisition tools, or CDP. For example, send a sushi promotion specifically to users who have previously ordered Asian food. [Learn more about segmentation ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) ### Omnichannel communication Using Customer Journey Builder allows combining channels based on customer preferences. * If a user opened a push notification > then don’t send promotion via other channels * If a user didn’t open a push notification > send an email * If a user didn’t open an email > send a WhatsApp message Similarly, you can leverage all other combinations of communication channels in your campaign. [Learn more about omnichannel campaigns ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) ## Prerequisites for sending WhatsApp messages Before you can send WhatsApp messages, you must complete the following steps: 1. **Set up the WhatsApp platform in Pushwoosh**. Connect your WhatsApp Business Account with Pushwoosh to enable messaging. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) 2. **Add WhatsApp subscribers**. Register phone numbers to build your WhatsApp audience. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp/) 3. **Ensure payment for WhatsApp messages**. Meta charges apply for each message sent via WhatsApp. Pushwoosh does not handle these payments, so ensure your billing information is updated in your WhatsApp account. Caution To send marketing templates from October 13, 2027, accept Meta’s Marketing Messages API terms for your WhatsApp Business Account. Utility, authentication, and service messages don’t need them. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#accept-metas-marketing-messages-api-terms) ## Create WhatsApp templates Before sending messages, ensure your required templates are created and approved in your WhatsApp Manager account. [Learn more about WhatsApp templates ](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) ## Send WhatsApp messages You can send WhatsApp messages in two ways: #### Send messages via the Pushwoosh Control Panel You can send WhatsApp messages directly from the Pushwoosh Control Panel, including specialized use cases: [Send one-time WhatsApp messages ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/) [Send WhatsApp messages via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/) [Send catalog messages ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp/) [Send promo codes ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-promo-codes-via-whatsapp/) #### Automate messaging via API Developer assistance needed Setting up WhatsApp messaging via API requires help from your development team. Please share the link below with them for proper implementation. [Learn more about sending WhatsApp messages via API ](https://docs.pushwoosh.com/developer/api-reference/whatsapp/#createwhatsappmessage/) --- # Register phone numbers for WhatsApp **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp/ Learn how to register phone numbers for WhatsApp in Pushwoosh --- For a customer to receive WhatsApp messages, they must have a valid phone number and be registered as a WhatsApp platform subscriber in Pushwoosh. Additionally, WhatsApp requires businesses to obtain opt-in consent from customers before messaging them. For guidelines on obtaining consent, refer to [WhatsApp Opt-In Best Practices](https://business.whatsapp.com/policy#best_practices). ## Methods to register WhatsApp subscribers Developer assistance needed *** You’ll need help from your development team to register WhatsApp contacts using the Pushwoosh SDK and API. Make sure to share the links below with them for detailed guidance. There are three main methods to register phone numbers as WhatsApp subscribers: * Using Pushwoosh SDK. [Learn more](https://docs.pushwoosh.com/developer/first-steps/subscribe-users#register-a-whatsapp-number) * Using Pushwoosh API. [Learn more](https://docs.pushwoosh.com/developer/first-steps/subscribe-users#whatsapp) * Importing WhatsApp contacts from a CSV file ## How to import WhatsApp contacts from a CSV file You can also upload contacts directly from a WhatsApp campaign: go to **Campaigns > WhatsApp messages > Upload contacts**. ![Upload WhatsApp contacts](https://docs.pushwoosh.com/whatsapp-messages-register-phone-numbers-for-whatsapp.webp) To import a contact list for later use across campaigns, go to **Audience > Import CSV > Import Contacts** instead. This shared [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) wizard can register email, SMS, WhatsApp, and Viber subscribers from the same CSV file in one pass. Map the column with phone numbers to the **WhatsApp** channel to register them as WhatsApp subscribers. Enter the plain phone number in [**E.164**](https://en.wikipedia.org/wiki/e.164) format. Pushwoosh adds the `whatsapp:` prefix internally. You can then use this data to create segmented and personalized WhatsApp campaigns. [Learn more about sending WhatsApp messages ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/) --- # How to send WhatsApp messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/ Send WhatsApp via one-time campaigns or Customer Journey. Choose immediate delivery or automated multi-channel flows. --- Before you send WhatsApp messages, ensure you have [configured WhatsApp](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) and [uploaded contacts](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp/). Pushwoosh offers two ways to send WhatsApp messages: #### One-time WhatsApp messages Send a single WhatsApp message to a segment, list of phone numbers, or all subscribers. Schedule it for now or a specific date and time. [Send one-time WhatsApp messages ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/) #### WhatsApp element in Customer Journey Add a WhatsApp step to a journey and combine it with push, email, in-app, or SMS. Trigger delivery by audience or events. [Send WhatsApp messages via Customer Journey ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/) Tip Use **one-time WhatsApp messages** for single campaigns and **Customer Journey** for automated, multi-channel flows. #### Specialized journey use cases [Send catalog messages ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp/) [Send promo codes ](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-promo-codes-via-whatsapp/) ## Send free-form messages and view your WhatsApp conversations To send free-form messages and view your WhatsApp conversations, navigate to the **WhatsApp Messages** section in Pushwoosh. Here, you can: * **View all active and past conversations** with customers to review message history and engagement details. Each conversation shows a countdown badge for the 24-hour customer service window opened by the customer’s last inbound message, tracked via the [WhatsApp Session Expires At](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) tag. * **Send messages directly** to customers by typing your message and clicking **Send** — while the window is open. Once it closes, the composer is disabled, and you can only continue the conversation with an approved [message template](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#use-a-pre-approved-template-from-your-meta-account). --- # Send catalog messages via WhatsApp **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp/ Learn how to send product catalog messages to your users via WhatsApp using Pushwoosh and Meta Commerce Manager. --- WhatsApp allows you to send product catalogs directly to users as part of your campaigns. This is done through the **Catalog** message type supported in Meta’s WhatsApp Business Platform. Pushwoosh integrates with this functionality, enabling you to trigger catalog messages from [customer journeys](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). ## Before you begin To send catalog messages via WhatsApp, please ensure you have completed the following steps: * **Integrated WhatsApp with Pushwoosh:** Your WhatsApp Business Account should be connected and configured in Pushwoosh. [Read how to set this up](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) * **WhatsApp payment method added:** Meta requires a valid payment method for sending messages. Ensure your payment information is current in your WhatsApp account. * **User contacts imported:** Upload your WhatsApp contact list to Pushwoosh. [Learn how to import contacts](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp#how-to-import-whatsapp-contacts-from-a-csv-file) ## Types of catalog messages Pushwoosh supports two types of WhatsApp catalog messages: * **Basic catalog message**: A simple catalog message that includes a **View Catalog** button. When tapped, users can browse your entire connected catalog in WhatsApp. [Learn more about catalog messages](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/marketing-templates/catalog-templates) * **Multi-product catalog message**: A message format that allows you to select products to be shown in the message, grouped into sections. Users can view product details and make selections directly within WhatsApp. [Learn more about multi-product messages](https://developers.facebook.com/documentation/business-messaging/whatsapp/templates/marketing-templates/mpm-templates) ## Create a catalog in Meta Commerce Manager Catalog messages allow you to connect your WhatsApp Business Account with a product catalog created and managed in Meta Commerce Manager. Once connected, you can send messages with a **View Catalog** button that lets users browse your product catalog. When a customer taps the **View Catalog** button, your product catalog appears within WhatsApp. This type of message is best for: * Promoting product collections * Enabling shopping directly from WhatsApp * Driving sales through personalized browsing experiences Learn more about [creating product catalogs in Meta Commerce Manager](https://www.facebook.com/business/help/1275400645914358?id=725943027795860) ## Link your catalog to WhatsApp Business Account You need to link your product catalog to your WhatsApp Business Account. For instructions, see the [Meta WhatsApp Business documentation](https://www.facebook.com/business/help/158662536425974). Note Only one catalog can be linked to a WhatsApp Business Account at a time. ## Create a catalog message template in WhatsApp Manager 1. In **WhatsApp Manager**, start creating a new template. 2. Choose **Category → Marketing**, and then **Catalog**. ![WhatsApp template selection screen showing Marketing category and Catalog message format](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-1.webp) 3. Enter the template name and language. 4. Then specify the catalog format. Select: * `Catalog message` (for basic catalog message) * `Multi-product message` (for product selection) ![WhatsApp template creation interface showing catalog format selection options](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-8.webp) If your catalog is already connected, it will appear under the **Catalog setup** section. Click **Manage catalog connection** to view or update the linked catalog. 5. Compose your message body (e.g., *“🛍️ Explore our latest collection! Tap below to view our catalog and discover what’s new — don’t miss out on exclusive deals.”*) ![Template editor showing message body field and catalog setup section](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-2.webp) 6. Submit the template for approval. Note Catalog templates do not support custom placeholders or media uploads. Product information and images are automatically pulled from your connected catalog. [Learn more about creating WhatsApp templates ](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) ## Send catalog messages with Pushwoosh ### Start building a customer journey 1. Go to **Customer Journey Builder**. 2. Click **Create Campaign** and select **WhatsApp** as the channel. 3. Choose the campaign type, such as a **Triggered campaign**. 4. On the Journey canvas: * Add an [Entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/) (for example, by event or audience). * Add a [WhatsApp element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/) to send the message. ### Configure the WhatsApp message element #### Basic catalog message In the **WhatsApp element**: 1. Set a clear step name (for example, `Catalog promo message`). 2. Select your approved **catalog template**. 3. Click **Apply** to save your changes. 4. Optionally, enable **Split flow based on message read status** to define different follow-up actions. ![Pushwoosh WhatsApp element configuration panel with catalog template selected](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-3.webp) #### Multi-product catalog message 1. In the **WhatsApp element**, select your approved MPM template. ![WhatsApp element configuration panel showing MPM template selection](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-5.webp) 2. Select a cover image. ![Cover image selection interface in WhatsApp element configuration](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-6.webp) 3. Configure the catalog section. For this, give each section a name and select up to 30 items per section. To select items, click **Add item** and select the items you want to display. Note You can create up to 10 sections, each with up to 30 items. ![Catalog section configuration interface showing section names and product item selection](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-7.webp) Frequency capping To limit how often users receive this WhatsApp message, use [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) in the element’s delivery settings. You can use global limits, send without capping, or set a custom limit. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#set-frequency-capping). ### Configure the rest of the journey End the branch by adding an [Exit element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection, or connect to [other Journey elements](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) to finish configuring your customer journey. Tip Make sure you review the flow and configure any additional steps needed before launching your campaign. ## Message examples Here’s how catalog messages appear to recipients: * Basic catalog message ![WhatsApp basic catalog message as seen by a recipient, displaying a View Catalog button](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-4.webp) * Multi-product catalog message ![WhatsApp multi-product catalog message as seen by a recipient, showing cover image and product sections](https://docs.pushwoosh.com/whatsapp-messages-send-catalog-10.webp) --- # Send one-time WhatsApp messages **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/ Send a one-time WhatsApp message from an approved template, or write a custom message for contacts with an open conversation. --- Before you start Connect your WhatsApp Business Account in **Settings → Configure Platforms → WhatsApp** before you create a campaign. [Learn how](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) To send a template, make sure at least one message template is created and approved in your WhatsApp Manager account. A custom message doesn’t need a template. To send a one-time WhatsApp message in Pushwoosh, navigate to the **Campaigns** section, select **One-time messages**, and click **Send message** > **WhatsApp**. ## Select WhatsApp content In the **Content** step, choose an approved [WhatsApp template](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) from the dropdown, or select **Custom message** at the top of the list to write your own text. The preview on the right shows the message as the recipient sees it. ![WhatsApp content step with template dropdown and message preview](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-1.webp) To change the order of templates in the dropdown, click the sort button above it. The dropdown is sorted by **Name A→Z** until you pick another order, and Pushwoosh remembers the order you pick and uses it in every WhatsApp template dropdown, in one-time messages and journeys alike. ### Write a custom message A custom message is free-form text you write in the campaign itself, without a template. WhatsApp delivers free-form text only inside the 24-hour customer service window opened by the user’s last inbound message, so a custom message goes only to contacts whose window is still open. To write a custom message: 1. In the template dropdown, select **Custom message**. 2. Enter the text in **Message text**. You can personalize it with user data and add emoji. The text can be up to 4096 characters. 3. Optionally, add an attachment in **Attachment URL**. Enter a public https link, or click the attachment icon to choose an image from your media storage. ![WhatsApp Content step with Custom message selected, message text filled in, and the message preview](https://docs.pushwoosh.com/whatsapp-one-time-custom-message.webp) With an attachment, the text is sent as its caption and can be up to 1024 characters. The link must end with a supported extension: * **Image:** JPEG or PNG, up to 5 MB. Media storage offers images only and rejects files over 5 MB. * **Video:** MP4 or 3GPP, up to 16 MB. * **Document:** PDF, Word, Excel, PowerPoint, or TXT, up to 100 MB. WhatsApp downloads the file from the link itself. If the link isn’t publicly accessible or the file doesn’t meet these requirements, the message isn’t delivered. When you select **Custom message**, the audience and the sending time are fixed. The campaign goes to the **WhatsApp: 24h window open** audience and is sent immediately. See [Select the audience](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#select-the-audience-for-your-whatsapp-campaign) and [Schedule WhatsApp message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#schedule-whatsapp-message). ### Send a rich message A [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/) you built in **Content > Messenger presets** can go out as a one-time WhatsApp message. 1. In the template dropdown, select the rich message. It has a **Rich message** badge and its Meta review state: **Approved by Meta**, **In Meta review**, **Rejected by Meta**, or **Not submitted to Meta**. 2. Check the message in the preview. Its text, media, variables, and buttons come from the rich message for each language, so there’s nothing to configure in the campaign. 3. Continue to the **Audience** step. What you can select there depends on the template: * **An approved rich message** goes to any audience you select. * **A rich message without an approved template** goes only to contacts with an open 24-hour window. The audience and the sending time are fixed, the same as for a [custom message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#write-a-custom-message). To send to any audience, [get the template approved](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#get-the-whatsapp-template-approved). ### Configure template variables If the selected template contains placeholders in the header, body, or a button URL, configure a value for each one before you can proceed. ![WhatsApp variable configuration panel with Tag and Custom text options](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-2.webp) For each placeholder, choose the **Variable Type**: * **Tag**: value from a user tag (e.g. `first_name`). Select the tag, a text-case modifier, and optional fallback text used when the tag is empty. The available modifiers depend on the tag’s type. String tags offer **CapitalizeFirst**, **CapitalizeAllFirst**, **UPPERCASE**, **lowercase**, and **regular**. Integer, Price, and Date tags offer currency and date formats instead. See [How to personalize a push](https://docs.pushwoosh.com/product/personalization/dynamic-content/#how-to-personalize-a-push) for the full list of modifiers by tag type. * **Custom text**: a fixed value entered manually. ### Select message type Choose how this WhatsApp message is treated for subscription and delivery rules: * **Marketing message**: Respects frequency capping. Not sent to [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) users. * **Transactional message**: Sent to all users regardless of subscription status. Bypasses frequency capping. Delivered to control group users. ![Marketing message and Transactional message options in the WhatsApp campaign form](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-3.webp) [Learn how message type affects delivery](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ## Select the audience for your WhatsApp campaign In the **Audience** step, define who will receive your WhatsApp message. For a [custom message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#write-a-custom-message), this step is locked. The audience is **WhatsApp: 24h window open**, every contact whose 24-hour customer service window is open right now, and the step shows how many recipients it has. If nobody has an open window, you can’t send the message. The count is checked again when you click **Send now**, because windows keep closing while the form is open. ![WhatsApp audience step with Send to segment, list of phone numbers, and all subscribers options](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-4.webp) ### Send to segment Target a predefined [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of your audience based on tags, events, or behavior. Select a segment from the dropdown, or click **Create segment** to [build](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) or [import](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/#how-to-import-a-segment) one. ### Send to a list of phone numbers Send the message to a specific list of phone numbers entered directly in the form. Phone numbers that are not yet in your project are automatically registered as WhatsApp devices and included in the send. ### Send to all subscribers Target your entire WhatsApp-reachable audience. Once you’ve set your audience, click **Next** to proceed to the scheduling step. ## Schedule WhatsApp message Choose when to send your WhatsApp message. A [custom message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/#write-a-custom-message) is always sent immediately, so the scheduling options are locked for it. ### Send immediately Send the message as soon as the campaign setup is complete. This is ideal for urgent or time-sensitive messages. ### Selected time Schedule your WhatsApp message for a specific date and time. * **Set the date and time**: Use the calendar to pick the date, then specify the time in hours and minutes (24-hour format). * **Select the timezone**: * **Subscriber’s device timezone**: The message is sent at the specified local time for each recipient. * **Custom timezone**: The message is sent at the same time for all recipients, useful for targeting a particular region. ![WhatsApp scheduling with Send immediately and Selected time options, date picker, and timezone selection](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-5.webp) ### Configure message delivery settings #### Set frequency capping Use [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) to limit how often users receive WhatsApp messages, preventing over-messaging and reducing opt-outs. Choose one of the following options: * **Use global frequency capping settings** Apply the project-wide limits configured in your [Global frequency capping settings](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping). For example, if the global limit is 3 messages in 9 days, additional messages exceeding this limit are skipped. ![Use global frequency capping settings option selected in message delivery configuration](https://docs.pushwoosh.com/push-notifications-one-time-push-25.webp) * **Send messages without global frequency capping** The user receives this message even if they’ve exceeded the channel’s message limits. Use with caution to avoid over-messaging. ![Send messages without global frequency capping option selected in message delivery settings](https://docs.pushwoosh.com/push-notifications-one-time-push-26.webp) * **Use custom frequency capping** Set a custom message limit for this message. If the user exceeds this custom cap, the message is skipped. Under **Counting mode**, choose how sent messages are counted: * **Rolling window**: counts the messages sent within the last N×24 hours before each send, where N is the number of days in the limit. For example, with a limit of 3 messages per 2 days, a message sent Monday at 9 AM stops counting on Wednesday at 9 AM. * **Calendar day**: counts the messages sent today and on the previous N-1 calendar days, starting at midnight. With the same limit of 3 messages per 2 days, on Wednesday every message sent since Tuesday midnight counts. Right after midnight, the window moves to Wednesday and Thursday. Select a **Capping timezone** to set when midnight falls. If you don’t select one, calendar days are counted in UTC. ![Use custom frequency capping with message limit fields and Counting mode set to Calendar day with a Capping timezone selector](https://docs.pushwoosh.com/push-notifications-one-time-push-27.webp) Note Frequency capping applies only to **Marketing** messages. When you choose **Transactional**, frequency capping doesn’t apply and these options are disabled. #### Set send rate limits The **Send rate** setting controls how quickly messages are delivered to your audience. Adjusting the send rate helps you manage delivery speed, prevent backend overload, and improve overall deliverability. Choose one of the following options: * **Use global send rate settings** Applies the send rate limits configured in your project’s message delivery settings. If no limits are set, all messages are sent immediately. [Learn more about global send rate limits](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ![Use global send rate settings option selected in send rate configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-1.webp) * **Send messages without global send rate** Sends messages as fast as possible, ignoring any global send rate limits. Use with caution to avoid delivery spikes. ![Send messages without global send rate option selected for maximum delivery speed](https://docs.pushwoosh.com/send-rate-in-journey-elements-2.webp) * **Use custom send rate** Overrides the global send rate for this message only. Specify the number of messages sent per minute for full control over delivery speed. ![Use custom send rate option with input field for messages per minute configuration](https://docs.pushwoosh.com/send-rate-in-journey-elements-3.webp) Once you’ve configured the options, click **Next** to proceed to the confirmation step. ## Review and send In **Review**, check the preview and the summaries for content, audience, and schedule. Update anything that still needs to change before you send. ![WhatsApp campaign review step showing message preview, audience, and schedule summary before sending](https://docs.pushwoosh.com/messaging-channels-whatsapp-one-time-6.webp) Click **Schedule** or **Send Now** to open a **Send this WhatsApp message?** dialog with a **Campaign name** field: a free-text label for this one send. Campaign name is prefilled with the selected template’s own body text, or with your text for a custom message (multi-line text prefills as a single line). Clear it and Message History falls back to naming the row after the template, the same as before this field existed. Keep it to 255 characters or fewer. Typing past the limit truncates the text without a warning. ![Send this WhatsApp message confirmation dialog with Campaign name field prefilled from the selected template's body text](https://docs.pushwoosh.com/whatsapp-one-time-confirm-sending.webp) Once set, the campaign name becomes this message’s bold title in [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) and its CSV export, in place of the template name. In the **One-time messages** list, a custom message row is titled **Custom**, the list’s short label for this message type, with its text shown underneath. Confirm again in the dialog to finish, depending on your selected scheduling options. ## Cancel or edit a scheduled WhatsApp message For WhatsApp messages that are scheduled and not yet sent (status **PENDING**), you can change or cancel them. Open the list of messages in **Campaigns** → **One-time messages**, then open the actions menu (⋮) for the message: * **Edit message**: opens the WhatsApp form with the message data loaded. You can update the content, audience, schedule, and delivery settings. In Review, the button is labeled **Apply changes** instead of **Schedule** or **Send Now**. Click it, then confirm again in the **Send this WhatsApp message?** dialog to save your changes, including any edit to the campaign name. * **Rename**: opens a small dialog to set or change the campaign name, without reopening the full edit form. Same 255-character limit as the Campaign name field you set at send time. Clear the field and save to remove the name. The row reverts to its previous default title. * **Delete message**: cancels the scheduled delivery and changes the status to **Canceled**. Tip You can also send WhatsApp messages using [Messaging API v2 `Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) with the [`whatsapp` payload block](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/payload-reference/#whatsapp-whatsapp). This requires assistance from your development team. Need catalogs, vouchers, or branching? One-time WhatsApp messages cover a single template or custom message send. For product catalogs, voucher codes, quick-reply branching, or split flows based on read status, use WhatsApp in [Customer Journey](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/) instead. ## Example scenario Imagine you run an e-commerce store and want to send a flash-sale alert by WhatsApp to customers who purchased in the last 90 days, scheduled for the morning the sale starts. **1.** Navigate to **Campaigns**, select **One-time messages**, click **Send message**, and choose **WhatsApp**. **2.** In the **Content** step, select the **Flash Sale Alert** template and fill in its placeholders. For example, map the body’s `{{1}}` variable to the `discount` tag with a `50%` fallback. Keep the message type as **Marketing** so it respects frequency capping. **3.** In the **Audience** step, choose **Send to segment** and select **Recent buyers (90 days)**. **4.** In the **Schedule** step, choose **Selected time**, set the date and time to the sale’s start, and select **Subscriber’s device timezone** so each customer receives it at the same local time. **5.** In **Review**, check the preview and the audience and schedule summaries, then click **Schedule** to finalize the campaign. --- # Send promo codes via WhatsApp **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-promo-codes-via-whatsapp/ Discover how to send personalized WhatsApp promo codes with dynamic or static placeholders using Pushwoosh. --- In Pushwoosh, you can send promo codes using WhatsApp message templates that include placeholders. These placeholders can be filled with coupon codes either dynamically (based on [user data or events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/)) or statically (with a fixed value). Learn how to configure such templates and insert the correct variable to deliver promo codes via WhatsApp. ## Before you begin Before you send promo codes via WhatsApp through Pushwoosh, make sure you have: * **Connected your WhatsApp Business Account to Pushwoosh:** [Learn how to configure WhatsApp integration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) * **Added a payment method to WhatsApp:** Meta requires an active payment method for sending WhatsApp messages. * **Imported your WhatsApp contacts into Pushwoosh:** [See instructions](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp#how-to-import-whatsapp-contacts-from-a-csv-file). ## Create a template in WhatsApp Manager First, [create a WhatsApp message template](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) and submit it to Meta for approval. Note WhatsApp templates must include only a placeholder for dynamic content. The actual value is inserted later through Pushwoosh, either dynamically (from an event or user property) or statically (as custom text). **Example template text** *Hi, your promo code `{{coupon code}}` is ready to use. Apply it at checkout and enjoy your discount!* ![Example WhatsApp message template showing promo code placeholder in Meta template editor](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-1.webp) When creating a message template in Meta, you can add a **Copy offer code** button that allows users to copy the promo code with a single tap. To configure it: 1. In the Meta template editor, open the **Buttons** section. 2. Click **Add button → Copy offer code**. 3. In the **Offer code** field, enter a sample static value (for example, `WINTER20`). This value is required only for Meta’s template review. During Journey execution, Pushwoosh automatically replaces this sample value with the actual promo code, either dynamically (from an event or user property) or statically (from custom text). ![WhatsApp Copy offer code button configuration in Meta template editor](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-2.webp) [Learn more about creating WhatsApp templates ](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) ## Create a WhatsApp campaign in Pushwoosh 1. Go to **Customer Journey Builder**. 2. Click **Create Campaign** and select **WhatsApp** as the channel. 3. Choose the campaign type (for example, a one-time or triggered campaign). 4. On the Journey canvas, add and configure an [Entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/). ## Set up the WhatsApp element In the **WhatsApp message** element, assign a clear step name (for example, `Promo_code_winter2025`). Under **Content**, select the approved Meta template you want to use. ![WhatsApp message element in Pushwoosh Customer Journey Builder with Content field](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-3.webp) ### Configure placeholders When you select the template, all placeholders defined in it will appear in the **Message** and **Action** fields of the WhatsApp element. Click on a placeholder to open the configuration panel, where you can choose the Variable type. This setting determines how the placeholder value will be populated, dynamically (from an event or user property) or statically (as custom text). ![Screen showing a WhatsApp message with the promo code placeholder visible in the template](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-4.webp) #### Configure the placeholders using Event property (recommended for dynamic codes) Use this option when the promo or coupon code is passed dynamically through an [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) sent from your system, for example, a triggered event after a user action. To insert a promo code dynamically into your WhatsApp message template: 1. Click the variable placeholder (for example, `{{coupon code}}`) in the message configuration. 2. In the **Variable type** dropdown, select **Event property**. 3. Under **Event**, choose the event that delivers the promo code. You can select any event used in the Journey, but make sure this event contains the promo code attribute (for example, `coupon_code`). 4. In the **Attribute** field, select the attribute that contains the promo code (for example, `coupon_code`). 5. In the **Fallback text** field, enter a default value (for example, `DISCOUNT10`). Pushwoosh retrieves the value of the selected attribute from the event and inserts it into the message template. ##### Example use case: rewarding high-value purchases Send a reward offer to users who complete a purchase worth more than $100. When a qualifying purchase is made, your backend sends a `high_value_purchase_completed` event to Pushwoosh. The event should include both the order value and a personalized promo code for the user’s next purchase. Example event: ```json { "request": { "application": "YOUR_APP_CODE", "userId": "user987", "event": "high_value_purchase_completed", "attributes": { "order_value": 125.50, "coupon_code": "THANKYOU20-FT9R" } } } ``` In your WhatsApp message template, configure the placeholder as follows: * **Variable type:** Event property * **Event:** high_value_purchase_completed * **Attribute:** promo_code * **Fallback text (optional):** SHOPMORE10 ![Event property configuration panel with Event and Attribute field selection](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-5.webp) Pushwoosh receives the event and triggers the corresponding Journey and inserts the value of the `promo_code` attribute into the message when the event is received. > **Message example** > > 🎁 Thanks for your order! Here’s a special code **THANKYOU20-FT9R** for 20% off your next purchase. This event-driven setup automatically rewards high-value customers right after checkout, encouraging repeat purchases with a personalized offer. #### Configure the placeholders using a user property Use this option if the promo or referral code is already stored in the user’s profile in Pushwoosh, for example, a unique referral, loyalty, or region-specific code assigned to the user in advance. [Learn more about tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/). In your WhatsApp message template: 1. Click the variable placeholder (for example, `{{coupon code}}`). 2. Set **Variable type** to **User property**. 3. In the **Property name** field, enter the key where the code is stored (for example, `coupon_code` or `Referral code`). 4. Add a **Fallback text** value to display if the property is missing for a user. Important If a user does not have the specified property (for example, the tag with the coupon code) and no fallback text is set, the message will not be sent to that user and they will drop off from this step in the Journey. Pushwoosh will automatically retrieve the value of this user property and insert it into the WhatsApp message when it is sent. ##### Example use case: sending personalized referral codes Send each user a personalized referral code stored in their Pushwoosh profile. This is useful when every user has a unique code assigned in advance, such as for referral, loyalty, or membership programs. When the campaign runs, Pushwoosh automatically retrieves the code from each user’s profile and inserts it into the WhatsApp message, allowing users to share their personal code with others to invite them or earn rewards. In your WhatsApp message template, configure the placeholder as follows: * **Variable type:** User property * **Property name:** Referral code * **Fallback text (optional):** JOIN2025 ![User property configuration panel with Property name and Fallback text fields](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-6.webp) When the message is sent, Pushwoosh replaces the placeholder with the user’s individual referral code stored in their profile. > **Message example** > > 🎁 Share your code **REF-A8F7Z** with friends and earn rewards when they sign up! #### Configure placeholders using Custom text (for static codes) Use **Custom text** when you want to send the same promo code to all users in your campaign. This option is best for large-scale promotions or limited-time offers that do not require personalization. To configure it: * Set **Variable type** to **Custom text** * Enter the static promo code All users will receive the same promo code when the message is sent. ##### Example use case: sending the same promo code to all users Send all users the same promo code for a seasonal winter sale. Because the code is the same for everyone, use the **Custom text** option to insert a fixed value directly into the WhatsApp message template. In your WhatsApp message template, configure the placeholder as follows: * **Variable type:** Custom text * **Value:** WINTER20 ![Custom text configuration panel with Value field for entering static promo code](https://docs.pushwoosh.com/whatsapp-messages-send-promo-codes-7.webp) When the message is sent, all users will receive the same promo code. > **Message example** > > ❄️ Enjoy our winter sale! Use code **WINTER20** at checkout and get 20% off all items. ### Set frequency capping To limit how often users receive this WhatsApp message, use [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) in the element’s delivery settings. You can use global limits, send without capping, or set a custom limit. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#set-frequency-capping) ### Split flow based on read status (optional) You can branch the Journey based on whether the user reads the WhatsApp message. Enable **Split flow based on message read status** in the WhatsApp element and set a waiting time (up to 7 days). After the waiting period: * **Read path:** send a follow-up message, promotion, or connect to **Exit**. * **Unread path:** send a reminder or retry message, then connect to **Exit**. This setup helps re-engage users who haven’t read your message and continue the flow for those who have. After you’ve finished configuring the WhatsApp element, click **Apply** to save your changes. ### Finish configuring the Journey and add conversion goals Complete the Journey setup by defining **conversion goals**, ending each branch with an **Exit** element or by leaving it without an outgoing connection, and configuring any additional follow-up logic. Review each step to make sure the correct message template, variable type, and trigger conditions are applied before activating the campaign. [Learn more about building customer journeys ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) --- # Send WhatsApp messages via Customer Journey **URL:** https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/ Send personalized WhatsApp messages with Pushwoosh Customer Journey. Build campaigns, use Meta templates, and add dynamic content. --- Send WhatsApp messages through Pushwoosh Customer Journey. You can select between audience-based and trigger-based campaigns, use templates from your Meta account, or create custom content within Pushwoosh. Tip For a single WhatsApp campaign sent right away or scheduled once, use [one-time WhatsApp messages](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/) instead. ## Prerequisites To send WhatsApp messages, ensure the following requirements are met: * **WhatsApp configuration completed**: Make sure your WhatsApp Business Account is integrated with Pushwoosh. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) * **A payment method for WhatsApp added**: Meta charges for each message sent via WhatsApp. Pushwoosh does not handle these charges, so make sure your **payment information** is updated in your WhatsApp account. * **Templates available**: Ensure that the required templates are already created and approved in your WhatsApp Manager account. [Learn more](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) * [WhatsApp contact list uploaded](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/register-phone-numbers-for-whatsapp#how-to-import-whatsapp-contacts-from-a-csv-file). ## Create a new WhatsApp campaign To create a campaign: 1. Go to **Campaigns > Customer Journey Builder > Create campaign > WhatsApp**. 2. On the journey canvas, choose the appropriate entry type based on your campaign goal: ##### Audience-based entry Target a predefined group of users based on filters like location, last activity, or purchase history. Ideal for planned, recurring, or re-engagement campaigns. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) **Examples:** * Send a promo to users inactive for 30 days * Announce a seasonal offer to loyal customers ##### Trigger-based entry Launch the journey when a specific event occurs, such as a user completing a purchase or booking. Ideal for real-time transactional messages or behavior-driven updates. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) **Examples:** * Confirm an order when status changes to “Ready for pickup” * Send a reminder 24 hours before an appointment ##### API-based entry Start the campaign programmatically by sending a POST request from your backend or external system. Ideal for automating messages based on external data or events. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/) **Examples:** * Notify a user after a CRM update * Send a WhatsApp offer after a survey completion ## Set up a WhatsApp message element Add the WhatsApp message element to the canvas, and provide a unique name to identify the message step. Next, configure the Whatsapp content in one of two ways: * Write a custom message. * Select a pre-approved WhatsApp message template from the dropdown. Templates are sorted by **Name A→Z**. To change the order, click the sort button above the dropdown. Pushwoosh remembers your choice for every WhatsApp template dropdown. The template must be available in the selected language and approved by WhatsApp. Learn more about [creating templates](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) or explore options in the [Templates library](https://www.facebook.com/business/help/722393685250070). Important You can initiate conversations only by using an approved Message Template. [Learn more](https://business.whatsapp.com/policy#best_practices:~:text=WhatsApp%20Business%20Platform%20Specific,Template%20at%20any%20time.) If a user sends a message, you can respond within the 24-hour period using custom content. ### Configure dynamic variables After selecting a template, Pushwoosh displays a preview of the message content. If the template contains placeholders, each one represents a dynamic variable that must be configured before the message can be sent. To insert a variable: 1. Click the `{}` placeholder in the message preview. 2. Select the **Variable Type**: * **User property**: Value from the user’s profile (e.g., `first_name`, `email`). * **Event property**: Value from a triggered event (e.g., `product_name`, `order_status`). * **Custom text**: Manually entered static value. 3. Select the relevant tag. 4. Set the **Text Case**. The options depend on the selected tag’s type. For a String tag: * **CapitalizeFirst**: Capitalizes the first word only (`"john doe"` → `"John doe"`). * **CapitalizeAllFirst**: Capitalizes each word (`"john doe"` → `"John Doe"`). * **UPPERCASE**: All uppercase (`"john doe"` → `"JOHN DOE"`). * **lowercase**: All lowercase (`"JOHN DOE"` → `"john doe"`). * **regular**: No formatting changes. Integer, Price, and Date tags offer currency and date formats instead — see [How to personalize a push](https://docs.pushwoosh.com/product/personalization/dynamic-content/#how-to-personalize-a-push) for the full list by tag type. 5. (Optional) Enter **Fallback Text** to be used if the variable is empty. 6. Click **Insert**. **Template example with fallback** ```plaintext Hi, {first_name}! Your {product_name} is now available with a {discount} discount. ``` If `first_name` is missing and fallback is `"there"`: ```plaintext Hi, there! Your shoes are now available with a 20% discount. ``` ### Configure action URLs WhatsApp message templates can include action buttons (e.g., “Buy Now”) that open a URL when tapped. These buttons can be personalized using dynamic variables inserted as placeholders in the URL. The action URL is defined during template creation in your Meta Business account. If the URL includes placeholders (e.g., `{product_id}`), you must assign a value to each placeholder in Pushwoosh before sending the message. To assign value to the placeholder: 1. Click the placeholder `{}` in the URL. 2. Choose the correct **Variable Type** (e.g., User property or Event property). 3. Select the appropriate variable (e.g., `product_id`). 4. (Optional) Add **fallback text** in case the variable is missing. 5. Click **Insert** to apply it. > **Important:** Make sure all placeholders in the action URL are assigned variables. The message won’t be sent if any placeholders are left unconfigured. ### Add media attachments When using a template that includes a media file (image, video, or PDF), please note that the media is not stored in the template itself. These files are uploaded only as examples during template submission to Meta for validation purposes. To successfully send the message in a live campaign, you must specify the actual file URL in Pushwoosh. To add media, enter a valid, publicly accessible URL pointing to the actual media file in the **Image** field. Example: `https://yourdomain.com/files/offer-banner.jpg` > **Important:** The message will not be sent if the media file URL is missing or inaccessible. ### Choose the message type In the WhatsApp element, select **Marketing message** or **Transactional message**: * **Marketing message**: For campaigns and promos. It respects frequency capping and the silence period. It excludes the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Transactional message**: For operational or service messages. Rules and limits don’t apply. It bypasses frequency capping. It is sent to the [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). No message type is preselected. Until you choose one, the element shows a **Message type not selected** warning. If the element is reached only from an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), **Marketing message** is shown with a **Default** badge. Choose **Transactional message** if the message is a service one. ![WhatsApp element message type selection with Marketing message and Transactional message options](https://docs.pushwoosh.com/whatsapp-messages-send-whatsapp-messages-11.webp) ### Set frequency capping Note Frequency capping options are available only when **Marketing message** is selected. When you choose **Transactional message**, frequency capping doesn’t apply and these options are disabled. Limit how often users receive WhatsApp messages using [global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) or a custom limit for the step. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#set-frequency-capping). ### Split the flow based on message read status (optional) You can split the journey based on whether the message was read or based on user interaction with message buttons (e.g., Quick Replies or Call-to-Action buttons). * To split by read status, enable the **Split flow based on message read status** option and configure the waiting time (up to 7 days). * To branch based on Quick Reply or Button interaction, configure the journey accordingly. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#configure-quick-replies) ### Use vouchers You can personalize WhatsApp messages by attaching a voucher code from a predefined pool. This is useful for promotions, discounts, and loyalty incentives.\ For this, create a WhatsApp template that includes the placeholder `{{voucher}}`. To include a voucher in your WhatsApp message: 1. Toggle **Vouchers** to ON in the WhatsApp message step. 2. In the **Voucher Pool** field, select the pool containing your available voucher codes. Make sure to add the pool of vouchers in advance. Learn more 3. (Optional) Use the **Assign Tag** field to apply a tag to users who receive a voucher. This tag can be used for segmentation or reporting. > **Important:** Make sure the selected voucher pool has enough codes for your campaign. If the pool runs out, the message will not be sent.\ > [Learn more about creating and managing voucher pools.](https://docs.pushwoosh.com/product/content/vouchers/) Once the WhatsApp element is configured, click **Save** and continue setting up your journey. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) ## Configure quick replies Quick replies in WhatsApp allow you to offer users predefined response options. With Pushwoosh Customer Journey Builder, you can set each quick reply to lead to a separate user journey flow, providing personalized follow-ups. This approach improves the user experience by offering clear, simple choices that match each user’s level of interest, making interactions smooth and personalized. To set up quick replies in your WhatsApp messaging flow: 1. In your Meta account, select an existing WhatsApp template or [create a new one](https://www.facebook.com/business/help/2055875911147364?id=2129163877102343) that includes Quick Reply buttons. For example, if your message is: ***“Are you interested in learning more about our new offer?”*** You could configure quick reply options like: * ***“Yes, I’m interested”*** * ***“No, thanks”*** * ***“Tell me more”*** 2. Next, after adding and [configuring the WhatsApp element](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#set-up-a-whatsapp-message-element) with a pre-approved template from Meta, set up a unique journey path for each quick reply option. For example: * If a user selects ***“Yes, I’m interested”***, they receive a follow-up message with details about the special offer or promotion. * If a user selects ***“No, thanks”***, the journey ends for them. * If a user selects ***“Tell me more”***, they receive a message with additional product information. * If a user does not respond within a set timeframe (e.g., 24 hours), set up an additional path to send a reminder message or end the journey. > **Important:** Make sure to configure all remaining steps of your journey before launching the campaign. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/) --- # Getting started with personalization **URL:** https://docs.pushwoosh.com/product/personalization/ Learn how to personalize messaging in Pushwoosh using Dynamic Content, Liquid Templates, and Multi-Language support for better engagement. --- Pushwoosh enables you to create highly tailored and engaging messaging experiences for your users. By using **Dynamic content**, **Liquid templates**, and **Multi-language** support, you can ensure your messages resonate with each recipient based on their preferences, behavior, and language. ## Dynamic content Personalize your messages by modifying the message content with corresponding **Tag values** collected from your users. For example, you can address users by their name or include their preferences to make each message highly relevant. [Learn more ](https://docs.pushwoosh.com/product/personalization/dynamic-content/) ## Liquid templates Take personalization to the next level with **Liquid Templates**. Add logic to dynamic content based on user attributes, behavior, or preferences. This allows you to send targeted, meaningful messages that resonate with each audience segment, ensuring higher relevance and improved campaign performance. [Learn more ](https://docs.pushwoosh.com/product/personalization/liquid-templates/) ### Liquid in in-apps Show each user their own data in an in-app shown by a journey: their Tag values, the attributes of the event that triggered the in-app, and their User ID. [Learn more ](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/) ### Connected content Fetch real-time data from external sources and insert it dynamically into your messages. Use it to personalize product recommendations based on user behavior or include unique promo codes from an external database, ensuring timely and relevant communication. [Learn more ](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) ## Multi-language Craft messages in multiple languages to engage a global audience. Multi-language support helps ensure your content is relevant across regions, increasing user engagement and satisfaction. [Learn more ](https://docs.pushwoosh.com/product/personalization/multi-language/) --- # Dynamic content **URL:** https://docs.pushwoosh.com/product/personalization/dynamic-content/ Personalize push and email messages with Dynamic Content to boost open rates and drive conversions by tailoring content to user preferences. --- [Youtube Video: Add a personal touch to your messages with user-centric Dynamic content](https://youtube.com/watch?v=A7l1_gK5yOA) ## What is Dynamic content This functionality allows you to personalize your push messages and emails by modifying the message content with corresponding Tag values that you collect from your users. You can address your user by name and use any of their preferences in the message to make it relevant for every single user. ## Use case Picture this: the application provides coupons for best restaurants in town every Friday. To draw users’ attention and bring them back to the app, you may send a basic push message that says “There are some coupons for you in the app, open it now!”. However, such a message doesn’t convey any specific information for a user, so it’s unlikely this push will be opened by everyone who receives it. To significantly increase the push open rate, notifications should be sent to the right audience with the right message. To send the most relevant messages, *collect the following information from users by calling* [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api#settags): * Name (sends *Alias* tag value to Pushwoosh) * Favorite Restaurant (sends *Restaurant* tag value) ## How to personalize a push With this info about users, you can personalize push messages by using Dynamic content. To add the Dynamic content to your message text, click the **Personalization** button on the top right of the input field. ![Personalization button located at the top right of the message input field](https://docs.pushwoosh.com/personalization-dynamic-content-1.webp) Note You can personalize not only the text of the message but other push content as well. You can personalize **titles**, **subtitles**, **headers**, **icons**, and **root params** from the platform-specific push settings. Also, **button URLs** for Chrome pushes can be personalized. The flow is the same as for push content, so follow the steps below to add a personal touch to every notification’s element. 1. Choose a Tag First, choose a Tag whose value will be used in the message. Pick an existing Tag from the list, or type a name that isn’t a Tag in your account yet, for example, a placeholder for a Journey attribute you plan to add later. To apply, click **Insert**. In our case, it’s the Name tag so that each user will receive a personalized push notification. ![Tag selection dialog showing available tags with Insert button to add selected tag to message](https://docs.pushwoosh.com/personalization-dynamic-content-2.webp) Caution Tags of type String, Integer, Price, and Date are available for personalizing pushes sent from Control Panel. To personalize with a List-type tag, [send messages via API](https://docs.pushwoosh.com/developer/api-reference/messages-api) or insert it into [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates). 2. Choose a format modifier Then, select a Format Modifier for the chosen tag value. The modifiers you can choose depend on the tag’s type. Since it’s the name (a String tag), capitalize the first character of the name, so “alexander” becomes “Alexander”. Every placeholder needs a modifier. If you leave it empty, the field turns red, and you can’t save the preset: you see “A personalization placeholder is incomplete: pick a modifier for it”. ![Format modifier selection showing CapitalizeFirst option for capitalizing the first letter of tag value](https://docs.pushwoosh.com/personalization-dynamic-content-3.webp) Note **Modifiers for String tags** * **CapitalizeFirst** – capitalizes the first letter in a tag value * **CapitalizeAllFirst** – capitalizes the first letters in all words in a tag value if the value consists of more than one word * **UPPERCASE** – switches all the letters to uppercase * **lowercase** – switches all the letters to lowercase * **regular** – inserts a tag value exactly as it’s set for a user or device, with no modifications Integer, Price, and Date tags have their own modifiers: * **Price tags:** regular, cent, dollar, comma, euro, jpy, lira * **Integer tags:** the same modifiers as Price, plus 8 date formats, because an Integer tag can hold a Unix timestamp * **Date tags:** the same 8 date formats, plus regular See the [Presets API reference](https://docs.pushwoosh.com/developer/api-reference/presets-api/#accepted-modifiers) for the exact date format strings. 3. Specify a default value You can also specify the Default Tag Value, which is used as a tag value in case a user didn’t specify it for some reason. Type in a default value for those who didn’t provide it, for example, “friend”. If a user has no value for the Tag and the placeholder has no default value, Pushwoosh doesn’t send them the message. ![Default tag value input field showing example default value for users who did not provide tag information](https://docs.pushwoosh.com/personalization-dynamic-content-4.webp) 4. Finally, click **Insert**. ![Insert button to finalize and add personalized tag to message content](https://docs.pushwoosh.com/personalization-dynamic-content-5.webp) ## Insert User ID Besides Tags, you can insert the [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) into message content. Pushwoosh resolves it for each recipient at send time, so you don’t need to store it as a Tag. If your app doesn’t set a User ID, the device’s HWID is used as its User ID. **User ID** is available in the **Personalization** list in: * [Push presets](https://docs.pushwoosh.com/product/content/push-presets/) * [App Inbox presets](https://docs.pushwoosh.com/product/content/app-inbox-presets/) * [Native in-app messages](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-dynamic-content) * [WhatsApp welcome message](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#set-up-an-automatic-welcome-message) To insert it: 1. Click **Personalization** on the field you want to personalize. 2. In the list, select **User ID**. To find it faster, use the search box. 3. Choose a format modifier. 4. Type a default value, for example, “friend”. 5. Click **Insert**. The default value works the same way as for Tags. The inserted placeholder looks like `{userid|regular|friend}`, where `regular` is the format modifier and `friend` is the default value. In [Liquid templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/), write `{{userid}}` instead. ## Personalization guides by channel If you want to personalize messages across multiple channels, check out our detailed how-to guides for different channels: [How to send a personalized push notification ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-push-notification/) [How to send a personalized in-app message ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message/) [How to send a personalized email ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-email/) [How to send a personalized SMS ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-sms/) --- # Liquid templates **URL:** https://docs.pushwoosh.com/product/personalization/liquid-templates/ Learn how to use Liquid templates in Customer journeys to add logic and dynamic content to your messages for advanced personalization. --- [Youtube Video: Learn how to use content templates in Customer journeys](https://youtube.com/watch?v=A7l1_gK5yOA) Liquid templates significantly broaden Pushwoosh’s personalization capabilities by implementing sophisticated logic in addition to regular[ Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) usage. Message personalization in Pushwoosh is based on [Tags (user data)](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags). Pushwoosh offers a variety of [default Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#default-tags) and [custom Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#custom-tags). Using them, you can specify a user’s first name, city, purchase history, etc. to send a more personalized message. For example: `Hi {{First_name}}, thanks for ordering {{item}}`. Liquid templates add more logic to dynamic content. For instance, if a user’s subscription tag contains “free,” you can send them a message: “Grab your 10% discount.” Modifying the message content according to users’ IDs, behaviors, and preferences is the most efficient way to increase relevance and get more impressive results from your marketing campaigns. ## Syntax Content templates based on [Liquid by Shopify](https://shopify.github.io/liquid/) use a combination of [**tags**](https://docs.pushwoosh.com/product/personalization/liquid-templates/#tags), [**objects**](https://docs.pushwoosh.com/product/personalization/liquid-templates/#objects), and [**filters**](https://docs.pushwoosh.com/product/personalization/liquid-templates/#filters) to load dynamic content. Content templates allow you to access certain variables from within a template and output their data without having to know anything about the data itself. Note To learn more about the syntax, please refer to [Liquid documentation](https://shopify.github.io/liquid/basics/introduction/). ### Objects `objects` define the content that will be displayed to a user. `objects` should be enclosed in double curly braces: `{{ }}` For example, when personalizing a message, send `{{Name}}` in its body to add the users’ names to the message’s content. The user’s name (Name tag value) will replace the Liquid object in a message the user will see. * Input ```plaintext Hi {{Name}}! We're glad you're back! ``` * Output Hi Anna! We’re glad you’re back! To insert the recipient’s User ID, write `{{userid}}` in lowercase. Variable names are case-sensitive, so `{{user_id}}` and `{{UserId}}` are unknown names and render as empty text. If a recipient has no User ID, `{{userid}}` also renders as empty text and the message is still sent. To show a fallback instead, add the `default` filter: `{{userid | default: "friend"}}`. ### Tags `tags` create the logic and control flow for templates. The curly brace percentage delimiters `{%` and `%}` and the text that they surround do not produce any visible output when the template is rendered. This lets you assign variables and create conditions or loops without showing any of the Liquid logic to a user. For example, using the `if` tag, you can vary the message’s language based on what language is set on user’s device: * Input ```liquid {% if Language == 'fr' %} Salut! {% else %} Hello! {% endif %} ``` * Output (fr) Salut! * Output (es) Hello! ### Tags operators | Operator | Description | | :--------: | -------------------------------------------------------------------------- | | `==` | equals | | `!=` | does not equal | | `>` | greater than | | `<` | less than | | `>=` | greater than or equal to | | `<=` | less than or equal to | | `or` | logical or | | `and` | logical and | | `contains` | checks for the presence of a substring inside a string or array of strings | Note In tags with more than one `and` or `or` operator, operators are checked in order *from right to left*. You cannot change the order of operations using parentheses — parentheses are invalid characters in Liquid and will prevent your tags from working. ### Filters `filters` modify the output of a Liquid object or variable. They are used within double curly braces `{{ }}` and variable assignment, and are separated by a pipe character `|`. Multiple filters can be used on one output, and are applied from left to right. * Input ```plaintext {{ Name | capitalize | prepend:"Hello " }} ``` * Output Hello Anna ## Liquid templates usage Liquid templates are available for both messages sent from Control Panel and [API requests](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates#using-liquid-templates-in-messages-sent-via-api). In Pushwoosh, Liquid templates are applicable to all content fields of any channel message: * Push notifications * Emails * In-app rich media shown by a journey, once Liquid in in-apps is enabled for your account To add a Liquid Template to your message, insert it into the message’s body. You can do it when working with [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) or [email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) elements, directly from the Customer Journey Builder interface. For in-apps, write Liquid in the in-app content itself. See [Liquid in in-apps](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/). To add data from your own backend to a push or email, such as a promo code or product recommendations, use [Connected content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/). Go to **Customer Journey Builder** > **Create Campaign** > drag and drop the following elements to your canvas: **Audience-based Entry**, **Push** (or **Email**), and **Exit**. Connect the elements. Then click the **Push** icon, choose **Custom content**, and insert your copy. To add Liquid logic, use tag values with the following syntax: ```liquid {% if TagName == 'value' %} Content to send in this scenario {% else %} Content to send otherwise {% endif %} ``` Then click **Apply**. [](https://docs.pushwoosh.com/personalization-liquid-templates-1.webm "Customer Journey Builder interface showing how to add Liquid template logic with if-else conditions to push notification content") Write Tag names without spaces. For a Tag named `My Tag`, write `{{MyTag}}`. Other characters stay as they are, so for the Tag `my_tag` write `{{my_tag}}`. [Learn more about Liquid templates in journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/dynamic-content-and-liquid-templates-in-journeys) Tip You can also use Liquid syntax in `/createMessage` requests to implement Liquid templates. For this, you’ll need assistance from your development team. Share the [Liquid templates guide](https://docs.pushwoosh.com/developer/guides/personalization/liquid-templates) with them for detailed guidance. --- # Connected content **URL:** https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/ Fetch data from your own backend, such as promo codes or product recommendations, and insert it into push and email with Liquid. --- Connected content is a feature in [Liquid templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) that allows you to dynamically retrieve and use data from an external source, such as a web service, directly within your email or push notification messages. This feature enables real-time personalization by fetching JSON data from a specified URL and saving it to a variable that can be utilized in your content. ## Key use cases * **Product recommendations**: Display personalized product lists tailored to each user. * **Promo codes**: Insert unique promo codes generated by a backend service. ## Prerequisites * To use Connected Content, you must have your own backend service that generates and provides the required data (e.g., promo codes, product recommendations) based on **User ID, HWID, or custom tags**. Pushwoosh then fetches this data before sending a message. ## Step-by-step implementation guide Developer assistance needed You’ll need your developer team’s help to use Connected content. Share this guide with them to get started. ### Step 1. Set up backend service The backend service should: * Accept a request containing user-specific parameters (e.g., `userId`). Connected Content supports `UserID`, `HWID`, or any custom tags you’ve set up in your project. * Return a JSON response with the required data. This content can then be inserted dynamically into messages How it works The backend service acts as a data provider, responding to HTTP requests with user-specific information. 1. Pushwoosh sends a request to your backend, passing user-specific identifiers as query parameters. 2. Your backend processes the request and retrieves the requested data. 3. Your backend returns a JSON response. 4. Before sending a message, Pushwoosh fetches the JSON response from the backend service and uses the returned values (e.g., the `code`) in the message content dynamically. **Example response** ```plaintext { "code": "SPECIALOFFERFORUSER12345" } ``` ### Step 2. Create a preset with Connected content in Pushwoosh 1. In the [Push](https://docs.pushwoosh.com/product/content/push-presets/) or [Email content editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/), insert the Connected Content syntax into the message field. **Example** ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }} :save result %} ``` **Syntax breakdown** | | | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `connected_content` | Fetches JSON data from the specified backend URL. | | `http://your-backend-url.com` | The backend endpoint that returns required data in JSON format. | | `userId={{ ${userid} }}` | A dynamic query parameter that passes the user ID to the backend. Inside `connected_content`, wrap a variable in `${}`. In the message text, write `{{userid}}` without it. | | `:save result` | Stores the fetched JSON response in the result variable for use in Liquid templates | ![Insert the Connected Content syntax](https://docs.pushwoosh.com/connectedcontent.webp) **Authentication (optional)** If your backend service requires authentication, you can include an API key or token in the Connected Content request to ensure secure access. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }}&auth=YOUR_API_KEY :save result %} ``` You can also send authentication (or any other) data as HTTP headers using the optional `:headers` parameter — a JSON object of header names and values. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }} :headers {"Authorization": "Bearer YOUR_TOKEN", "X-Api-Key": "YOUR_API_KEY"} :save result %} ``` | | | | ---------------- | ------------------------------------------------------------------------------------------ | | `:headers {...}` | A JSON object of HTTP headers sent with the request, e.g. `Authorization: Bearer `. | Static values only `${}` personalization variables work only inside the URL. Values inside `:headers` are static and are not interpolated. Keep credentials out of the URL Prefer `:headers` over a URL query parameter (e.g. `&auth=YOUR_API_KEY`) for API keys and tokens: if the fetch fails, the request URL is written to logs, exposing a key placed there. Values inside `:headers` are never logged. Connected content also requires Liquid content templates to be enabled for your account. If they aren’t, the whole `connected_content` tag — including any key inside it — is sent to every recipient as literal text instead of being rendered. Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you’re unsure whether your account has this enabled. **Using tags in Connected content** To include a tag value, add it to the URL as a query parameter in the same `{{ ${tag_name} }}` form. Written as `{{ tag_name }}` inside the `connected_content` tag, the value is sent empty. ```plaintext {% connected_content http://your-backend-url.com?userId={{ ${userid} }}&lang={{ ${Language} }} :save result %} ``` 2. Next, add the message text incorporating the **retrieved data**, like this: ```plaintext Hey, {{userid}}, grab your personal promo code - {{result.code}} ``` ![Add the message text with the retrieved data](https://docs.pushwoosh.com/connectedcontent-1.webp) 3. After finalizing the message content and configuring the preset settings, save it for reuse in campaigns. [](https://docs.pushwoosh.com/connectedcontent-2.webm "Send a message with connected content") ### Step 3. Send a message using the configured preset Send a message with this preset using the [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#how-to-send-a-push-notification-using-the-one-time-push-form) or [email form](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/) or [customer journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Important If the service returns a status other than HTTP 200 OK, the email or push notification will not be sent. This ensures that your communication only goes out if the necessary data is successfully retrieved. --- # Liquid in in-apps **URL:** https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/ Personalize in-apps shown by a journey with Liquid. Show each user their Tag values, event attributes, and User ID. --- Write [Liquid](https://docs.pushwoosh.com/product/personalization/liquid-templates/) in the content of an in-app to show each user their own data: their Tag values, the attributes of the event that triggered the in-app, and their User ID. For example, greet the user by name and show the promo code passed in the event. Note Liquid in in-apps is off by default. To enable it for your account, contact your Customer Success Manager or support. ## Where Liquid works in in-apps Liquid works only in an in-app that a journey shows with the [In-App element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/). Both **Show in-app** modes work: **On next app launch** and **On event**. Write the Liquid in one of these places: * The text of a built-in editor block, such as an [HTML block](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#html) or a **Text** block. Text in every language of the in-app works. * The `index.html` file of an uploaded HTML archive. * A text field of a [native in-app](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/). ## Add Liquid to an in-app To add Liquid, write it in the in-app editor, then select the in-app in a journey. 1. Open the in-app in the built-in editor. 2. Add an **HTML** block, or select an existing one. 3. In the **HTML** field, type the Liquid code by hand. ![HTML block settings in the in-app editor with Liquid code that greets the user by firstName, shows the loyalty_tier Tag in upper case, the promo_code event attribute, and an if condition for the gold tier](https://docs.pushwoosh.com/personalization-liquid-templates-8.webp) 4. Click **Save**. 5. In the **In-App** element of your journey, select the in-app in **Media Source**. 6. If the in-app uses event attributes, choose **On event** in **Show in-app** and select the event. The editor preview shows the Liquid code as written. Each user sees their own values. Note The **Merge tag** button doesn’t insert Liquid. It inserts a Dynamic content placeholder such as `{firstName|regular|}`. Placeholders and Liquid work together in one in-app. In this example, `firstName` and `loyalty_tier` are Tags, and `promo_code` is an attribute of the event that shows the in-app: * Input ```liquid

Hi, {{ firstName | default: "there" }}!

Your {{ loyalty_tier | upcase }} code: {{ promo_code }}

{% if loyalty_tier == "gold" %}

Shipping is on us today.

{% endif %} ``` * Output Hi, Anna! Your GOLD code: AUTUMN-42 Shipping is on us today. ## Data you can use These values are available in the Liquid of an in-app: | Value | What it is | How to write it | | ------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------- | | [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) | A piece of user data stored in Pushwoosh, such as the user’s name. The in-app shows the value it has when the in-app is shown. | `{{ loyalty_tier }}` | | Event attribute | An attribute of the event selected in **On event** mode of the In-App element. If an attribute has the same name as a Tag, the attribute value is used. | `{{ promo_code }}` | | User ID | The ID your app assigns to the user. | `{{ userid }}`, in lowercase | | HWID | The hardware ID of the user’s device in Pushwoosh. | `{{ hwid }}`, in lowercase | ## Write variable names Follow these rules when you type a variable name: * Type the name exactly as it is in Pushwoosh, including capital letters. A name that doesn’t match, for example `{{ firstname }}` for the Tag `firstName`, shows as empty text. * Write Tag and attribute names without spaces: `{{ LoyaltyTier }}` for a Tag named `Loyalty Tier`. * To show a fallback instead of empty text, add the `default` filter: `{{ firstName | default: "there" }}`. ## Change how a value looks To change how a value looks, add a [Liquid filter](https://shopify.github.io/liquid/filters/abs/). All standard filters work, for example `upcase`, `capitalize`, `date`, and `truncate`, and so does the Pushwoosh `timezone` filter. ## Limitations of Liquid in in-apps In an in-app, Liquid has these limits: * If you send the in-app as an Instant in-apps campaign (**Campaigns** > **Instant in-apps**), Liquid isn’t applied and users see the code as written. Use [Dynamic content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) placeholders instead. * `{% include %}` isn’t supported. Write the full code in the in-app itself. * [Connected content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) isn’t supported. Use the values from [Data you can use](https://docs.pushwoosh.com/product/personalization/liquid-templates/liquid-in-in-app/#data-you-can-use) instead. * If the in-app archive is larger than 1 MB, Liquid isn’t applied and the user sees the code as written. * If the Liquid code has an error, for example an unclosed `{% if %}`, the user sees the in-app with the code as written. Check the in-app on your own device before you launch the journey. --- # Multi-language messaging **URL:** https://docs.pushwoosh.com/product/personalization/multi-language/ Boost engagement with multi-language messaging. Send push notifications, emails, and SMS in users' native languages for global audiences. --- [Youtube Video: Be closer to your audience by talking to users in their native language](https://youtube.com/watch?v=A7l1_gK5yOA) ## What is multi-language messaging? Multi-language messaging allows you to send push notifications and other messages that automatically display in each user’s preferred language. By providing content in multiple languages, you ensure that messages are delivered in a way that feels natural and relevant to every user. If a user’s language doesn’t match any of the defined localizations, the message will be shown in the default language you set. ## Creating multi-language messages Pushwoosh allows you to send multi-language messages across various communication channels, including: * [Push notifications](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push) * [Emails](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/#define-the-languages-you-want-to-use-in-your-email) * [SMS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-languages) * [LINE](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/line-presets/#define-languages) * [In-apps](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#localize-the-template) This helps you deliver localized content to users in their preferred language, making it especially useful for apps with an international audience. Note The language selectors count differently depending on the channel. Push and in-app selectors count devices. For example, the in-app selector counts devices on platforms that can show in-app content. Email, SMS, LINE, and WhatsApp selectors count contacts instead, by the value of their Language tag. Push and in-app devices usually get the Language tag automatically, from the device’s OS locale. Email, SMS, LINE, and WhatsApp contacts have no device to read it from, so set the tag explicitly. For email, that’s the `language` parameter in [registerEmail](https://docs.pushwoosh.com/developer/api-reference/email-api/#registeremail). For any channel, you can also set the tag later via [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). A contact without the tag isn’t counted for any language and receives the default-language message. A Language tag set only at the user level, not tied to a specific device or contact, doesn’t appear in any of these selectors either. #### Example: Creating multi-language push notifications In the [Push preset form](https://docs.pushwoosh.com/product/content/push-presets/), first, create the default message content, which is sent to all users unless a more specific language version is provided. You can then add content for additional languages to tailor the message to users’ device language settings. *For example, if your default language is English but you want to reach Spanish and German speakers as well, add those languages to your push notification. Users with Spanish or German phone settings will then receive the message in their preferred language, while others will see the default English version.* To add additional languages, click **Plus** at the top of the editor, select the languages in the **Select languages** window, and click **Apply**. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push) ![Language selection interface showing Plus button to add additional languages to push notification content](https://docs.pushwoosh.com/shared-36.webp) Tip By default, the **Select languages** window lists the languages your devices already use. To add a language no device uses yet, for example when you’re planning to enter a new region, select **Include languages without devices** in that window. Next, create push content for each language you’ve chosen. [Learn more](https://docs.pushwoosh.com/product/content/push-presets#create-push-content) Instead of typing every language by hand, you can [import translations from a CSV or JSON file](https://docs.pushwoosh.com/product/content/push-presets/import-export-translations/) directly in the editor, or manage translations for many presets at once from your own scripts through the [Presets API](https://docs.pushwoosh.com/developer/api-reference/presets-api/). Note Don’t forget about the payload limitations for some of the platforms! Different languages use different alphabets, and while your English version may fit in nicely, the push in Arabic text may exceed iOS quota. ## Language code standard Language tag value is a *lowercase* two-letter code according to the [ISO-639-1](http://en.wikipedia.org/wiki/List_of_ISO_639-1_codes). Devices with Language tag values inconsistent with the ISO-639-1 will receive the push message with default language localization. ## Implementing localization via API This feature is also available in our API as a parameter of the `/createMessage` requests. For this, you’ll need assistance from your development team. Please share [this guide](https://docs.pushwoosh.com/developer/api-reference/messages-api) with them for detailed instructions. Tip If you want to see the full stats for your currently registered languages, check out the [stats for your Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#tags-statistics). ## Use cases for multi-language messaging Use multi-language push notifications, emails, SMS, and LINE messages to: * Launch products across multiple regions with a single campaign * Run promotions tailored to specific regions or markets * Send onboarding or welcome messages in each user’s native language * Notify global audiences of important updates or policy changes with localized content ## Best practices for multi-language messaging * Keep content culturally relevant and tone-appropriate. * Test on devices set to different locales before launching campaigns. * Track engagement metrics by language to refine future messaging. * Consider text length for each translation * Avoid relying solely on machine or AI-generated translations. Always review them carefully or use professional human translators to ensure accuracy, tone, and contextual relevance. ## FAQ: Multi-language messaging #### What is multi-language messaging? Multi-language messages automatically display in a user’s preferred language based on their device settings. For example, if your campaign supports English, French, and Spanish, a user whose device is set to French will receive the French version. #### What happens if a user’s language isn’t available? They’ll receive the message in your chosen default language. #### Can I test multi-language push notifications before sending? Yes. You can preview each language in the [content editor](https://docs.pushwoosh.com/product/content/) and [test delivery](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/) on devices set to different locales. #### Can I use machine translation for message localization? You can start with machine or AI translations, but reviewing them manually is highly recommended to ensure quality, especially for important messages. --- # Pushwoosh AI tools overview **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/ Explore AI-powered tools in Pushwoosh for content generation, automated translation, audience segmentation, personalized targeting, and automation of cross-channel campaigns. --- AI tools in Pushwoosh are built to simplify your workflow and are powerful enough to automate nearly every aspect of your process, from content generation to full campaign creation, using intuitive language prompts. Below is an overview of the available AI capabilities. ## AI Assistant Beta feature notice As a beta feature, AI Assistant may occasionally experience errors, interruptions, or incomplete functionality. AI Assistant is a chat-based assistant built into the Control Panel. Describe what you need in plain language, and it searches documentation, answers questions about your account, and, once you grant consent, performs actions across the areas you work in every day. Key areas include: * **Documentation:** Quickly get information from Pushwoosh help articles and guides. * **Campaigns and presets:** Create and manage presets and campaigns across channels. * **Audience and segmentation:** Manage user data, segmentation, and custom attributes and tracking events. * **Customer journeys:** Create, update, and control automated user journeys. * **Content:** Build interactive in-app messages and manage message presets. * **Analytics and reporting:** View message statistics, create dashboards, and export data. * **Account administration:** Manage authentication, access control, users, permissions, and third-party services. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) ## Use ManyMoney AI in your favorite AI client Connect ManyMoney AI to Claude Desktop, Cursor, Windsurf, or any other [MCP](https://modelcontextprotocol.io/)-compatible client and drive your Pushwoosh project from there. Build segments, presets, and customer journeys, diagnose delivery issues, and analyze campaign performance — all from natural-language prompts in the AI tool you already use. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/) ## Send messages from your AI agent Give your AI agent the power to deliver push notifications, email, SMS, WhatsApp, Telegram, and more through Pushwoosh — directly from any MCP-compatible client. The Messaging MCP server handles the actual sending so your agent can trigger notifications in response to events, user actions, or agent decisions without any manual steps. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/) ## Generate push content with AI Composer Use the AI Composer to quickly generate effective push notification text. Provide minimal input, and the tool will create optimized messaging based on best practices and user intent. [Learn more ](https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer/) ## Translate push content with AI Easily translate push content into multiple languages using AI, helping you scale international campaigns without manual translation. [Learn more ](https://docs.pushwoosh.com/product/content/push-presets/translate-push-content-with-ai/) ## Use AI tools in the Drag & drop editor Pushwoosh’s [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/) uses AI throughout the email-building workflow: a chat-based AI tab for generating and editing content, AI image and alt text generation, AI-drafted subject lines and preheaders, auto-translate, and brand-matched styling via Brand book’s Derive from brand. [Learn more ](https://docs.pushwoosh.com/product/content/email-content/ai-tools-in-email-editor/) ## Build segments with AI Create smarter audience targeting by building segments with AI. The system analyzes user behavior and automatically suggests high-impact segment rules for better campaign performance. [Learn more ](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai/) --- # AI Assistant overview **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/ AI Assistant is a chat-based assistant in the Pushwoosh Control Panel that searches documentation and acts in your account. --- Beta feature notice As a beta feature, AI Assistant may occasionally experience errors, interruptions, or incomplete functionality. **AI Assistant** (also called **ManyMoney AI**) is a chat-based assistant built into the Pushwoosh Control Panel. Describe what you need in plain language, and it searches Pushwoosh documentation, answers questions about your account, and, once you grant consent, calls Pushwoosh APIs to build segments, journeys, presets, campaigns, and more on your behalf. You decide what changes in your account. AI Assistant starts work only when you ask, and before most changes it shows a card for you to review and approve. See [Work with AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/#approve-actions-before-they-run) for how approval works. ## What AI Assistant can do | Area | What it does | | :---------------------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Documentation** | Searches Pushwoosh help articles and guides to answer your questions. | | **Application management** | Creates, configures, and deletes applications and platform settings. Deleting an application requires confirmation, the same as any other destructive action. | | **Campaigns and presets** | Creates and manages presets and campaigns for push, email, SMS, and more. WhatsApp presets are created in Meta WhatsApp Manager. AI Assistant can list the presets available to a journey and use them there, but it can’t create or edit them. | | **Audience and segmentation** | Builds and updates segments, manages tags and device information, and tracks custom events. | | **Customer journeys** | Creates, updates, and controls automated journeys. | | **Content** | Builds in-app messages and rich media, and generates or edits email templates from a chat prompt. | | **Analytics and reporting** | Pulls message statistics and delivery reports, exports campaign data, builds custom dashboards, and, with Stripe connected, pulls revenue and subscription statistics. | | **Files and reports** | Exports answers and data as PDF, CSV, or Markdown files and replies with a download link. | | **Data analysis** | Runs calculations over exported statistics, such as cohorts, funnels, and revenue roll-ups, in an isolated sandbox with no network access. | | **Troubleshooting** | Diagnoses delivery, journey, segmentation, and integration issues, including checks before you launch a campaign. See [Troubleshoot with AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/troubleshoot-with-ai/). | | **Account administration** | Manages users, groups, access permissions, certificates, domains, third-party integrations, and per-channel delivery rate and frequency-capping limits. | | **Developer tools** | Registers test devices for campaign testing, sets up geozones for location-based messaging, and manages voucher pools and codes. | | **Web** | Reads public web pages you link to and searches the web when Pushwoosh documentation doesn’t cover a question. | | **Memory** | Remembers durable facts about your account across conversations, such as your preferences and business context. Ask it to forget a fact at any time. | | **Support** | Opens a support ticket on your behalf when you ask it to. | To reverse a change, ask for the opposite one AI Assistant has no undo button. To reverse something it did, ask it to make the opposite change, or make the change yourself in the Control Panel. See [Reverse a change](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/#reverse-a-change). ## Usage limits AI Assistant is included in Pushwoosh plans, and each plan has its own request limit. For current limits by plan, see [Pushwoosh pricing](https://www.pushwoosh.com/pricing/). If you don’t see AI Assistant in your Control Panel, contact your Pushwoosh account manager. When you reach your plan’s limit, a banner appears above the chat input, and you can’t send messages until you act. Click **View usage** in the banner to open **Billing** in your account settings, then open the **AI wallet** tab. ![Usage limit banner above the chat input, with a link to view usage](https://docs.pushwoosh.com/ai-assistant-16.webp) To view usage or top up the AI wallet, you need billing access. If you don’t have it, ask the person who manages billing for your Pushwoosh account. ## Learn more [Work with AI Assistant ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/)Turn it on, send requests, approve its actions, attach files, and use voice. [Prompt library ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-prompt-library/)Prompting best practices and examples by task. [Troubleshoot with AI Assistant ](https://docs.pushwoosh.com/product/pushwoosh-ai/troubleshoot-with-ai/)Get a full diagnosis of delivery, journey, and segmentation issues. [ManyMoney AI MCP server ](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/)Use the assistant from Claude Desktop, Cursor, and other MCP clients. --- # Work with AI Assistant **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/ Turn on AI Assistant, send requests, approve its actions, attach files, use voice, and manage conversations in Pushwoosh. --- Use this page to start working with AI Assistant and to check, approve, or reverse the changes it makes in your account. ## Get started ### Grant consent To let AI Assistant act in your account, grant consent first. Without consent, it can still answer questions and search documentation, but it can’t read your account data or make changes. 1. Log in to your Pushwoosh account. 2. Click the **AI Assistant** button. ![AI Assistant button in Pushwoosh Control Panel](https://docs.pushwoosh.com/ai-assistant-8.webp) 3. At the top of the chat window, click the gear icon. 4. Select **Data processing** from the menu. ![Gear icon settings button at the top of the chat window with the Data processing menu item open](https://docs.pushwoosh.com/ai-assistant-9.webp) 5. In the Data processing consent window, select **Full Automation**. 6. Click **Apply**. ![AI Assistant settings window showing Full Automation option selected with Apply button](https://docs.pushwoosh.com/ai-assistant-1.webp) #### Full Automation and General Assistant modes What AI Assistant can do depends on whether you’ve granted consent: | Mode | What AI Assistant can do | | :------------------------------------ | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Full Automation** (consent granted) | Reads your account data and makes changes on your behalf. Your prompts, generated responses, and tool calls go through authorized AI subprocessors. | | **General Assistant** (no consent) | Answers questions, explains how features work, searches Pushwoosh documentation and best practices, and offers guidance based on what you describe. It can’t access your account data, build campaigns, or make changes. | To switch modes, click the gear icon at the top of the chat, select **Data processing**, choose a mode, and click **Apply**. ### Start a conversation 1. Click the chat name at the top of the AI Assistant window. 2. Select **New conversation** from the dropdown menu. ![New conversation option in AI Assistant conversations dropdown menu](https://docs.pushwoosh.com/ai-assistant-2.webp) 3. Pick a suggested prompt, or type your own request in the chat input. For more examples, see [Prompt library](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-prompt-library/). ![AI Assistant new-conversation screen showing Creation, Analysis, and Configuration prompt categories with suggested prompts](https://docs.pushwoosh.com/ai-assistant-13.webp) ## While AI Assistant is working ##### Stop a response To stop AI Assistant in the middle of a response, click **Stop generation**. ##### Continue an unfinished request AI Assistant can take only a limited number of steps (tool calls and replies) per request, so a large request can stop halfway. When this happens, a banner with a **Continue** button appears. Click **Continue** to let AI Assistant finish the request from where it stopped. Until you click **Continue** or send another message, the request stays unfinished. ##### Free up space in a long conversation The chat input shows how full the conversation is. When it’s almost full, click **Compact context** in the chat input. AI Assistant summarizes the conversation to make room for new messages. ## Approve actions before they run AI Assistant asks for your approval before it sends messages or changes your account. It shows a Decision card in the chat, and nothing changes until you approve it. ##### Actions that need approval * Sending messages * Updating or deleting objects * Changing configuration, billing, or access * Editing email templates * Changing frequency capping * Creating objects with a wide reach: API tokens, account invites, popups, rich media, and third-party integrations ##### Actions that run without approval * Creating most new objects: segments, journeys, tags, geozones, campaigns, voucher pools, test devices, and more. A segment or journey created this way starts as a draft and doesn’t affect users until you launch it. Other creates, like a tag or a geozone, take effect right away, since they add something new rather than change or remove existing data. * Turning on test-device registration from the app. It switches itself off after 1 hour. * Any action in the one-time onboarding chat. ##### Approve or reject an action 1. Read the Decision card that appears in the chat. It shows the action, why AI Assistant wants to make it, and a warning if the action can’t be reversed. For sending a message or starting a journey, it also shows how many users it reaches, once that’s known. Once you approve, the action runs right away, with no second confirmation. ![Decision card in the chat asking to approve deleting a test tag, with Approve and Reject buttons](https://docs.pushwoosh.com/ai-assistant-17.webp) 2. Click **Approve** to run the action, or **Reject** to cancel it. Either way, AI Assistant reports the result in the same message. ![Chat after clicking Approve, showing the card marked Approved and AI Assistant confirming the tag was deleted](https://docs.pushwoosh.com/ai-assistant-23.webp) The action runs with the permissions of whoever approves it. Once a card is decided, it shows who approved or rejected it and when. To run a rejected action later, ask AI Assistant to propose it again. ##### Approve several actions at once When AI Assistant proposes several actions in one reply, they appear on one card, with a checkbox next to each action. All checkboxes are selected at first. The approve button reads **Approve all** when every action is selected and **Approve selected** when only some are. * To run every action on the card, click **Approve all**. * To run only some of them, clear the checkboxes of the actions you don’t want, then click **Approve selected**. The cleared actions are rejected, and the card is marked **Partially approved**. * To cancel every action on the card, click **Reject all**. ![Decision card with three tag deletions, two of them selected, and the Approve selected and Reject all buttons](https://docs.pushwoosh.com/ai-assistant-38.webp) ##### When a card expires An unanswered card expires after 24 hours, or as soon as you send a new message in the same conversation. A new message expires every pending card in that conversation, and the expiry time is shown on each card. AI Assistant has to propose an expired action again, so approve or reject pending cards before you send your next message. ### See all pending cards The **Decisions** panel lists pending cards from all conversations in the current project. To open it: 1. Click the gear icon at the top of the chat. 2. Select **Show proposal cards**. ![Gear menu showing Data processing and Show proposal cards options](https://docs.pushwoosh.com/ai-assistant-14.webp) If nothing is pending, the panel shows “Nothing is waiting for a decision”. To approve or reject a card from the panel, click **Open in chat** on it and decide in the chat. ## Check what AI Assistant did The conversation is the only record of what AI Assistant did. There’s no separate activity log. For each request, the conversation shows: * Action counts, such as **Created · 3 actions** or **Failed · 1 action** * A **Reasoning** section Before you rely on a result, make sure it’s what you asked for. If it isn’t, ask AI Assistant to adjust it and add the details your first request left out. Deleting a conversation removes this record for good, so keep conversations with important changes. ## Reverse a change To reverse something AI Assistant did, ask it to make the opposite change. For example, ask it to pause a journey it resumed or delete a segment it created. You can also make the change yourself in the Control Panel. Some changes can’t be reversed, because sent messages and deleted objects are gone for good. Before deleting a segment, check that no journey uses it. Deleting one that a journey uses doesn’t show a warning, and the journey loses its entry segment. ## Attach files To add files to your request, click **+** in the chat input, or drag the files onto the chat input. ![Chat input showing the highlighted drop zone that appears when dragging a file onto it to attach](https://docs.pushwoosh.com/ai-assistant-11.webp) * **Formats:** `.p12`, `.pem`, `.p8`, `.cer`, `.json`, `.txt`, `.pdf`, and `.csv`. * **Size:** up to 10 MB per file. * **Number of files:** you can attach several files at once. ## Ask by voice Ask a quick question by voice instead of typing, for example, how a campaign performed or why a journey stalled. 1. Click the phone icon at the top of the chat window to start a voice call. ![Phone icon in the chat window header for starting a voice call with AI Assistant](https://docs.pushwoosh.com/ai-assistant-12.webp) 2. Ask your question. 3. Click the phone icon again to end the call. What the call keeps depends on where you start it: * **In an open conversation:** the call continues it with the same context, and you can switch back to typing at any point. * **Before you open a conversation:** the call starts without context and doesn’t save a transcript. ## Manage conversations Conversations are saved automatically and stay available after you reload the page or switch tabs. Use the chat name dropdown at the top of the window to switch between them or delete them. Tip Start a separate conversation for each project or topic, for example, “Abandoned Cart Journey: Shoes” and “Onboarding Campaign Setup”. ### Switch between conversations 1. Open the chat name dropdown. 2. Click the name of an existing conversation to switch to it. ![Switching between existing conversations in AI Assistant dropdown menu](https://docs.pushwoosh.com/ai-assistant-7.webp) The current conversation name is shown in the dropdown title bar. ### Open a conversation from a notification If you leave the tab while AI Assistant is still working, you get a web push notification when it finishes or reaches its step limit. Click the notification to open the chat in that conversation. ### Delete a conversation 1. Open the chat name dropdown. 2. Hover over the conversation you want to delete. 3. Click the **trash icon** to remove it. Deleting a conversation is permanent This permanently removes its history and cannot be undone. --- # AI Assistant prompt library **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-prompt-library/ Prompting best practices and example prompts for AI Assistant, covering segments, journeys, content, and analytics tasks. --- Writing clear, specific prompts helps [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) provide accurate, relevant, and actionable responses. Use the following strategies and examples to improve your results. Example prompts are grouped by task, the same way as the suggested prompts in a new conversation: **Creation**, **Analysis**, and **Configuration**. ## Prompting strategies and best practices | Do | Don’t | Why | | :------------------------------------------------------------------------------------ | :------------------------------ | :------------------------------------------------- | | Be specific about what you want | Give vague instructions | Clear requests get accurate results | | Include identifiers (application codes, campaign names, journey UUIDs, segment names) | Reference resources vaguely | Lets AI Assistant target the right object | | Write detailed prompts with background, outcome, and constraints | Give one-line requests | More context means fewer follow-up rounds | | Use consistent naming for events, journeys, tags, and campaigns | Mix naming conventions | Avoids confusion when AI Assistant looks things up | | Add clear descriptions to custom events, segments, and tags | Leave them undocumented | Helps AI Assistant interpret them correctly | | Verify results after every action or response | Assume it worked | Catches mistakes before they compound | | Read each step before moving on, especially for permanent changes | Rush through multi-step actions | Permanent changes need review before they happen | | Review a Decision card’s reason before approving it | Approve without reading | The card is your last chance to catch a mistake | Not every action goes through a Decision card Creating new objects, such as segments, journeys, geozones, and voucher pools, runs right away. See [Actions that run without approval](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/#actions-that-run-without-approval). For these, verify the result right after AI Assistant replies. ### Effective and less effective prompts | Effective prompt | Less effective prompt | | :---------------------------------------------------------------------------------------------------------------------------------------------- | :---------------------------- | | *”Create a segment for users who abandoned cart in the last 7 days."* | *"Fix my pushes."* | | *"Show me push notification statistics for campaign ‘Summer Sale’ from June 1 to 15."* | *"Show me stats."* | | *"Help me debug why my iOS push notifications aren’t delivering."* | *"Something’s wrong."* | | *"Journey ‘Welcome Flow’ (UUID: abc-123) stopped sending emails yesterday at 3 PM."* | *"My journey isn’t working."* | | *"Build a segment for users who viewed a product but did not purchase in the last 14 days. The goal is to target them with a follow-up email."* | *"Help me target users.”* | ## Creation Build something new. The chat’s own starter prompts for this category: *“Build a complete marketing strategy”*, *“Create an onboarding journey”*, *“Create a re-engagement push campaign”*. ### Segments and audience > *“Create a segment of users who have not opened the app in the last 30 days and have made at least one purchase in the past, excluding those who have unsubscribed from notifications.”* **What AI Assistant does:** builds the segment with the filter conditions described. ![AI Assistant created segment for inactive users with past purchases excluding unsubscribed](https://docs.pushwoosh.com/ai-assistant-4.webp) **Check after:** confirm the segment size looks reasonable and that excluded users (here, unsubscribed users) are actually excluded. ### Customer journeys ##### Build a journey from a description > *“Create a journey that starts when a customer adds shoes to their cart and leaves without making a purchase.* > > *Wait 1 hour to check if a purchase occurs. If it does, send a thank you message and exit the journey. If not, send a first reminder and wait 23 more hours.* > > *If the customer completes the purchase during this period, send a thank you message and exit. If they still haven’t purchased, send a second reminder with a discount, wait 1 more hour, and then exit the journey.”* **What AI Assistant does:** builds the full journey with the branching logic described. ![AI Assistant generated customer journey flow for abandoned cart campaign with reminders](https://docs.pushwoosh.com/ai-assistant-3.webp) **Check after:** open the journey in the builder and confirm the branches, delays, and messages match your intent before activating it. ##### Edit a journey you already created In the same conversation, ask for a change to the journey. AI Assistant edits the existing journey and its messages instead of creating new ones. > *“Now change the discount on the second reminder from 10% to 15%.”* **What AI Assistant does:** shows a Decision card with a before/after table for exactly what changes (push title, push text, step name), and flags what to check: the new discount code has to exist in your store, and a bigger discount costs more margin per order. Nothing changes until you approve. ![Decision card showing the before and after of a journey's second reminder push after raising its discount](https://docs.pushwoosh.com/ai-assistant-34.webp) **Check after:** open the journey and confirm it still has the same two messages, with only the second one’s content changed. If the journey is already running, pause it first. AI Assistant can’t edit a running journey, and nothing reaches users while it’s paused, so plan the edit for a quiet moment. ### Content ##### Generate an email template > *“Create email content for an online store aimed at reactivating previous purchasers by offering special incentives such as discounts or exclusive offers. The goal is to encourage repeat purchases and re-engage customers who haven’t shopped recently. Focus on clear, compelling messaging that highlights the value of the offer and includes a strong call to action.”* **What AI Assistant does:** saves the generated email as a template and replies with a link to open it in the email editor. It doesn’t add images itself, but an image block’s **Generate with AI** panel in the editor can, from a short description you write there. ![AI Assistant generated email content for reactivating previous purchasers with discount offer](https://docs.pushwoosh.com/ai-assistant-5.webp) **Check after:** review the copy, images, and links in the editor before sending a test. ##### Edit an existing email template Once a template exists, you don’t need to recreate it from scratch to make changes. Ask AI Assistant to edit specific parts (content blocks, subject, preheader, sender, or palette), and it updates the saved template in place. > *“In the ‘Win-back: 20% off for returning customers’ email template, change the subject line to mention the 20% discount more directly, and replace the ‘Free gift wrapping’ card with a new one about easy, no-hassle returns.”* **What AI Assistant does:** proposes both changes as a single Decision card, with the old and new subject lines and the replaced card’s text spelled out. The template updates once you approve it. The rest of the content stays untouched. ![AI Assistant's explanation of an email template edit, showing the old and new subject lines and the replaced card's heading and text](https://docs.pushwoosh.com/ai-assistant-24.webp) **Check after:** confirm the subject line and the replaced card look right, and that nothing else in the template changed. ### In-app messages and rich media > *“Create an in-app message that shows on app launch, with the headline ‘Welcome back!’, a close button, and one CTA button labeled ‘See what’s new’ linking to the app’s home screen.”* **What AI Assistant does:** proposes a Decision card with the full content and layout, including any guessed values (such as a deep link) flagged for you to confirm. The in-app saves once you approve. It isn’t shown to anyone until it’s also attached to a trigger, such as an app-launch event in a journey. AI Assistant doesn’t add images to an in-app. The editor’s own Image field only accepts a URL or a file from media storage, with no built-in generator. ![The AI-generated "Welcome back!" in-app modal with a product image, close button, and CTA, added through the editor's Image field](https://docs.pushwoosh.com/ai-assistant-25.webp) **Check after:** confirm the content and any links are correct, then attach it to a trigger before it can reach users. ### Vouchers and promo codes > *“Create a voucher pool named ‘Spring Sale’ with 10 single-use promo codes, no expiration date.”* **What AI Assistant does:** creates the pool directly, without a Decision card. AI Assistant writes the codes itself (or uses the ones you list in your prompt) and then reports them back along with how many are still unused. Pushwoosh guarantees each code goes to only one user. It doesn’t track whether a code was actually redeemed, so enforcing single redemption is up to your own checkout or backend. ![Voucher pool created with 10 unused codes, no expiration date](https://docs.pushwoosh.com/ai-assistant-26.webp) **Check after:** confirm the pool has the expected code count before using it in a message or journey. ### Geozones > *“Create a circular geozone named ‘Downtown Store’ centered at [latitude], [longitude] with a 500 meter radius.”* **What AI Assistant does:** creates the geozone directly, without a Decision card, and reports back its status and range. A message is optional. Without one, the zone fires the `PW_GeozoneEntered` event when a device enters it. Use that event to start a journey, and pick the message and audience there. A minimum gap between repeat entries (cooldown) is required by the API, with no default. If you don’t specify one, AI Assistant picks a value rather than leaving it unset, so a person lingering near the zone’s edge doesn’t retrigger it repeatedly. ![Geozone list showing the newly created geozone, active with a 500 m range](https://docs.pushwoosh.com/ai-assistant-28.webp) **Check after:** confirm the location and radius match what you intended, and that the app has location permission and the SDK’s location feature enabled. A coordinate of exactly 0 (a point on the equator or the prime meridian) is currently rejected as missing. ## Analysis Investigate data. Starter prompts: *“Diagnose underperforming campaigns”*, *“Analyze revenue by segment and channel”*, *“Find drop-off points in user journey”*. ### Analytics and dashboards ##### Get a campaign performance report > *“Generate a comprehensive report of message delivery rates, open rates, and click-through rates for all push notification campaigns launched in this app, segmented by campaign type and message content.”* **What AI Assistant does:** returns the report broken down by campaign type and message content, with delivery, open, and click-through rates for each, and flags data-quality caveats it finds along the way, such as push click-through rate being calculated from opens (Pushwoosh has no separate push click count) or a delivery count that’s under-reported because of a missing iOS setting. ![AI Assistant's performance report broken down by campaign type, with sends, deliveries, opens, and rates for each](https://docs.pushwoosh.com/ai-assistant-37.webp) **Check after:** spot-check a couple of campaigns against numbers you already know. ##### Build a dashboard > *“Create a dashboard for this in-app campaign only: impressions, clicks on the bottom button, and completions of event X by unique users.”* **What AI Assistant does:** asks which in-app you mean and for the real event name if either is missing, flags that dashboards can’t filter by in-app directly or count clicks on a single button, then builds the closest match: the three requested metrics as widgets (using an “interactions” proxy for the button-click metric), plus a trend chart. **Check after:** confirm the dashboard is scoped to only this in-app, and that the interactions widget isn’t double-counted as the exact button click. ##### Recalculate a segment > *“Recalculate the segment ‘High-value churners’ and tell me how many users match now.”* **What AI Assistant does:** starts a recalculation and reports the updated user count once it’s ready. **Check after:** compare the new count against what you’d expect given recent activity. ### Revenue analysis ##### Calculate week-over-week growth > *“Pull delivery and revenue numbers for the last 30 days and calculate week-over-week growth.”* **What AI Assistant does:** returns delivery and revenue totals for the period, along with the week-over-week growth rate, when the account has revenue data to compute one from. If nothing is tracked (no connected payment provider, no revenue-bearing event firing), it says so instead of reporting a rate. ![AI Assistant returned delivery and revenue totals for the last 30 days with week-over-week growth](https://docs.pushwoosh.com/ai-assistant-20.webp) **Check after:** check the date range matches what you asked for. ##### Compare a journey against its control group > *“Compare this journey’s revenue against its control group.”* **What AI Assistant does:** checks whether the journey has its own holdout split and whether a revenue-bearing event is set up. If either is missing, it explains exactly what to add rather than guessing a result. If the account only has an account-wide control group, it says that group measures all marketing at once and isn’t a substitute for a journey-level holdout. **Check after:** confirm the holdout percentage and that it’s attached to the journey itself, not relying only on the account-wide control group. ##### Find campaigns with no revenue > *“Find campaigns from the last 30 days that haven’t generated any revenue.”* **What AI Assistant does:** checks whether revenue is tracked at all before answering. If it isn’t, every campaign would trivially match, so it reports the tracking gap and the account’s real send activity instead of a meaningless “all campaigns” list. **Check after:** if revenue tracking is already set up, confirm the date range and that paused or draft campaigns aren’t included unless you asked for them. ### Account strategy > *“Analyze my account and propose a revenue-focused plan: target segments, channels, and campaign timeline.”* **What AI Assistant does:** audits the account for what could block revenue (tracking gaps, unreachable audience, empty segments) before building the plan. When the audit comes back clean, as it does here, it moves straight to which segments to target, which channels to use, and a campaign timeline, for you to review before acting on any of it. ![AI Assistant's account audit confirming revenue tracking and reachable audience, followed by a target-segment table](https://docs.pushwoosh.com/ai-assistant-21.webp) **Check after:** confirm the suggested segments and timeline make sense for your current campaigns, and address any data gaps the audit flags before trusting later numbers. ## Configuration Set something up. Starter prompts: *“Set up mobile SDK (iOS/Android)”*, *“Set up web SDK”*, *“Set up email sending”*. ### Delivery settings > *“Set push frequency capping for this app to 1 message per user per week.”* **What AI Assistant does:** proposes the new push frequency-capping limit for the whole application as a Decision card. The limit changes once you approve it. Frequency capping applies per application and channel, not per segment or campaign. ![Message delivery settings showing the frequency cap updated to 1 message per 7 days](https://docs.pushwoosh.com/ai-assistant-33.webp) **Check after:** confirm the new limit in the application’s frequency-capping settings, and check it doesn’t starve a multi-step journey of its later messages. ### Test devices > *“Register my phone as a test device for push testing on iOS.”* **What AI Assistant does:** turns on **Add test devices from the app** on the **Test Devices** page for 1 hour. The time left is shown next to the toggle. While it’s on, opening the app isn’t enough by itself: send it to the background and bring it back to the foreground 6 times within 30 seconds, and the phone registers itself. If you already know the phone’s push token or device ID (HWID), add it to your prompt, and AI Assistant registers the device directly. ![Test Devices page with the "Add test devices from the app" auto-registration window active](https://docs.pushwoosh.com/ai-assistant-27.webp) **Check after:** confirm the new device appears in the test device list with the right platform. ### Integrations > *“Create a webhook integration that posts to [your server URL] when a message is sent.”* **What AI Assistant does:** checks the app’s current webhook setup first, since each app has only one and creating a new one replaces it, then proposes a Decision card with the target URL, covered events, and auth header state. The card says so if it would overwrite something. The integration starts firing only after you approve it. ![AI Assistant's webhook setup summary: URL, events, auth header, and the overwrite check it ran first](https://docs.pushwoosh.com/ai-assistant-35.webp) **Check after:** confirm the target URL, covered events, and scope (all messages vs. specific ones) match what you intended, and add an auth header if your server should verify the calls are really from Pushwoosh. ### Users and access > *“Invite [email] to this account with [group] access.”* **What AI Assistant does:** proposes a Decision card naming the group the invite assigns. Permissions are per group, each with See, Modify, and Send push rights per application. A group isn’t a fixed role with hardcoded capabilities, so what a group can actually do depends on how your account has configured it. The invite email only goes out once you approve. ![Permissions page showing the account's group/permission matrix (Approver, Analyst, Editor, Full user) with See, Modify, and Send push columns](https://docs.pushwoosh.com/ai-assistant-30.webp) **Check after:** open the group’s permissions (as in the screenshot above) and confirm See, Modify, and Send push are set the way you want for this person, application by application. ### Support > *“Open a support ticket about this issue and include the details from this conversation.”* **What AI Assistant does:** creates a support ticket and confirms once it’s submitted, as an alternative to navigating to [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) yourself. **Check after:** note the ticket number. Pushwoosh Support follows up from there. ## Related For diagnosing a specific problem, like a stalled journey or a failed delivery, see [Troubleshoot with AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/troubleshoot-with-ai/), which has prompt templates organized by issue type. --- # ManyMoney AI MCP server **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/ Drive your Pushwoosh project from Claude Desktop, Cursor, and other MCP clients to manage apps, campaigns, journeys, and analytics with AI. --- Beta feature notice The ManyMoney AI MCP server is in beta. The set of available actions, the endpoint, and authentication options may change. The ManyMoney AI MCP server lets you drive your Pushwoosh project from the AI assistant you already use. Connect it to a [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) client such as Claude Desktop, Cursor, or Windsurf, and ManyMoney AI can plan, build, launch, and analyze Pushwoosh work on your behalf from natural-language prompts in your editor or chat. If you have used [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) inside the Pushwoosh Control Panel, the MCP server gives you almost the same assistant outside the Control Panel. ## What you can do Once you connect the MCP server, your AI client can work across the Pushwoosh feature areas you use every day. | Area | What you can do | | ----------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Knowledge base** | Get answers about channels, journeys, integrations, and best practices grounded in the live Pushwoosh documentation instead of digging through the help center mid-task. | | **Applications and account** | Launch a new app for a new market, add Android to an iOS-only project, issue an API token for a partner integration, or grant a teammate the right level of access without context-switching out of your chat. | | **Audience and segmentation** | Build the segment you need on demand, like *users who bought in the last 30 days but haven’t opened the app in 7*. Define the events you target on, tune frequency capping, and adjust the control group as traffic ramps. | | **Content** | Draft the push, email, SMS, and LINE presets for a campaign in one go, build rich-media in-app messages with personalized offers, update the translations on an existing rich-media template without rebuilding it, and route email links through your own tracking domain so the brand stays consistent. | | **Customer Journey** | Spin up a Welcome flow, an Abandoned Cart recovery, or a Win-Back series. Pause one that isn’t pulling its weight. Clone a winner and adapt it for a new market, then pull the per-step funnel to see where users drop off. | | **Diagnostics** | Stuck on *“why didn’t this push land for these iOS users yesterday?”* Hand it to the assistant. It traces the message, correlates platform settings and the preset, and explains what to fix in plain English. | | **Analytics and revenue** | Build the dashboard your CMO actually asks for, compare a journey’s revenue lift against the control group, and break a campaign down by app, country, or cohort without writing a single SQL query. | | **Integrations** | Connect Stripe and start segmenting by payment events the same day, sync Meta for cross-channel retargeting, plug in Segment, Piano, Hubspot, Shopify, or a custom webhook, and check the connection status from the chat. | | **Vouchers and rewards** | Drop ten thousand promo codes into a pool, wire it to your Welcome journey, and watch redemption stats land. This is handy for launches, win-back campaigns, and partner promotions. | Important Sending live messages is intentionally not exposed through the ManyMoney AI MCP server to prevent accidental broadcasts. Use the assistant to plan and build your campaigns, then trigger the actual broadcast from the Pushwoosh Control Panel, from a Customer Journey you have published, or — if your workflow requires an agent to send messages programmatically — from the [ManyMoney Messaging MCP server](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/). ## Use cases #### Build a campaign end-to-end from your editor Ask your assistant: *“In application `XXXXX-XXXXX`, create a segment of users who added a product to the cart in the last 7 days but did not purchase. Then create a push preset ‘Cart Reminder — discount 10%’ and a Customer Journey that targets this segment, sends the preset 1 hour after entry, and exits if a `Purchase` event is recorded.”* Your assistant assembles the segment, the preset, and the journey. You review and publish the result in the Pushwoosh Control Panel. #### Diagnose why a push did not arrive While debugging in your AI client, ask: *“In application `XXXXX-XXXXX`, why did message `m-12345` fail for device `d-67890`? Explain in plain English.”* The assistant investigates the delivery, correlates platform settings and the message preset, and explains the failure plus suggested fixes. #### Analyze and report on revenue Ask: *“Compare revenue and conversion for users who completed Journey `Welcome Flow` versus the control group over the last 30 days, broken down by application.”* The assistant assembles the comparison from your project’s data so you can keep iterating on the analysis or paste the result into your weekly report. #### Onboard a new project from a brief Provide a short product brief and ask the assistant to *“create the application, add iOS and Android platforms, set up an Email From address, register a test device, create the standard Welcome and Cart Abandonment journeys with sensible defaults, and list everything you created so I can review.”* ## Compatible AI clients The ManyMoney AI MCP server works with any client that implements the MCP specification, including: * **Claude Desktop** by Anthropic * **Cursor** and **Windsurf** * **Cline** and **Continue** * **ChatGPT Desktop** with MCP support enabled * Custom integrations built on top of the MCP specification ## Connect the MCP server ### Step 1. Make sure you have a Pushwoosh account You need an active Pushwoosh account with access to the applications you want the assistant to work with. The MCP server runs every action under your Pushwoosh permissions, and your AI client can only do what you can do in the Control Panel. ### Step 2. Add the server to your AI client Use the following endpoint in your client’s MCP configuration: ```plaintext https://manymoney.pushwoosh.com/mcp ``` * Claude Desktop Add the server to your Claude Desktop config file (`claude_desktop_config.json`): ```json { "mcpServers": { "manymoney": { "url": "https://manymoney.pushwoosh.com/mcp" } } } ``` Restart Claude Desktop. On first use, the app opens a browser window so you can sign in to Pushwoosh. * Cursor / Windsurf Add the server to your `.cursor/mcp.json` (or the equivalent file in Windsurf): ```json { "mcpServers": { "manymoney": { "url": "https://manymoney.pushwoosh.com/mcp" } } } ``` Reload the editor. The first request triggers a browser sign-in to Pushwoosh. * Other clients For any other MCP-compatible client, point it at `https://manymoney.pushwoosh.com/mcp` and follow the client’s instructions for adding a remote MCP server. ### Step 3. Authenticate The MCP server supports two authentication methods: | Method | When to use it | | ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **OAuth 2.0** | Recommended for desktop and editor clients. The client opens a browser, you sign in with your Pushwoosh account, and the token is stored locally. | | **API token** | Recommended for headless or scripted setups. Send the header `Authorization: Token ` with every request. Create and manage API tokens in the Pushwoosh Control Panel. | ### Step 4. Try it out Open a new chat in your AI client and ask something concrete and scoped to one of your applications: > *In application `XXXXX-XXXXX`, list the customer journeys created in the last 30 days, and for each one show whether it is currently running and how many users entered it.* If the connection works, your assistant pulls the data from your project and returns a grounded answer. ## Tips and best practices * **Provide application codes and identifiers.** The same prompting tips that work in [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-prompt-library/#prompting-strategies-and-best-practices) work over MCP. Reference applications by code, journeys by UUID, and segments by name to keep the assistant scoped. * **Destructive actions aren’t available over MCP.** A tool call that would normally need your approval (deleting data, archiving a journey, rewriting a segment) returns an error here instead, directing you to the AI Assistant chat in the Control Panel, where it goes through the Decision-card review. * **Keep the connection scoped per project.** Most MCP clients let you enable or disable individual servers per workspace. If you only want Pushwoosh available in a specific repo or document, configure it there. * **Stay in your safety zone.** Sending live messages is intentionally not available over MCP. Plan, build, and review with the assistant. Trigger the actual broadcast from the Pushwoosh Control Panel or an automated journey. ## Data and your AI provider The MCP server runs every action under your Pushwoosh permissions and applies the same rate limits and account scoping as the Control Panel. It does not, however, control what your AI client sends to its model provider. Your prompts, the tool arguments your assistant builds, and the Pushwoosh API responses it reads all flow through the AI client and the model vendor you have chosen — Anthropic, OpenAI, or whoever your client uses — and their privacy, retention, and training policies apply. If you need Pushwoosh to handle the model side of the conversation under its own data handling rules, use [AI Assistant in the Control Panel](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) instead. There, Pushwoosh routes prompts only through authorized subprocessors. ## Related [AI Assistant inside the Control Panel ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) [Troubleshoot with AI Assistant ](https://docs.pushwoosh.com/product/pushwoosh-ai/troubleshoot-with-ai/) [AI tools overview ](https://docs.pushwoosh.com/product/pushwoosh-ai/) --- # ManyMoney Messaging MCP server **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/ Give your AI agent the power to send push notifications, email, SMS, WhatsApp, Telegram, and more through Pushwoosh, directly from any MCP-compatible AI client. --- Beta feature notice The ManyMoney Messaging MCP server is in beta. The endpoint and authentication options may change. ## Overview The ManyMoney Messaging MCP server is the sending counterpart to the [ManyMoney AI MCP server](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/). Where the ManyMoney AI MCP server helps you plan and build campaigns, the Messaging MCP server gives your AI agent the power to **actually deliver messages** — push notifications, email, SMS, WhatsApp, Telegram, LINE, Kakao, and more — on demand, from natural-language instructions. Connect it to any [Model Context Protocol (MCP)](https://modelcontextprotocol.io/) client and your agent can fire a push to a segment or send a personalized email to a specific user, all from a single natural-language request. ### Supported channels | Channel | Message types | | ------------------ | ------------------------------------------------------------------------------------------------------------------------- | | **Push — iOS** | Standard, silent, and critical notifications, VoIP pushes, and Live Activities | | **Push — Android** | Standard pushes, plus Huawei and Amazon Fire | | **Push — Web** | Web push in Chrome, Firefox, Safari, and Edge (Windows) | | **Email** | HTML body, templates, attachments, custom From/Reply-To | | **SMS** | Text messages through the SMS platform configured on your account | | **WhatsApp** | Meta-approved templates for outbound sends; free-form text only within the 24-hour window after a user messages you first | | **Telegram** | Text messages with content variables | | **LINE** | Content and template messages | | **Kakao** | Content and template messages | ### How it works 1. Connect the Messaging MCP server to your AI client once (see [Connect the MCP server](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/#connect-the-mcp-server) below). 2. In your AI client, open a new chat and describe the send in plain language. Include the application, the audience (segment or specific users), the message text, and when to send if it is not immediate. See [what to include in your request](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/#what-to-include-in-your-request) below. You do not need to build API requests or JSON yourself. 3. When the agent is ready to send, review the details and approve the action in your client. 4. After you approve, Pushwoosh sends the message and returns a [message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code). Use it in [Message history](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) to find the send and track delivery and statistics. ### What your agent can do After setup, the agent can: * **Send on any supported channel:** push (iOS, Android, Web), email, SMS, WhatsApp, Telegram, LINE, or Kakao. * **Reach a segment:** broadcast to everyone in a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/). * **Reach specific users:** send to one or more [user IDs](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#user-id) after an event (order update, password reset, and similar). * **Schedule delivery:** send now, at a set time, after a delay, or in each user’s local timezone. * **Personalize content:** fill template placeholders such as `{{first_name}}` or `{{promo_code}}` per recipient. * **Count sends toward a campaign:** tell the agent which [campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to use. Delivery and engagement for that send show up under that campaign in the Control Panel. See [example chat requests](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/#example-prompts) in [Send messages from your AI client](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/#send-messages-from-your-ai-client) below. ### Compatible AI clients The Messaging MCP server works with any MCP-compatible client, including: * **Claude Desktop** by Anthropic * **Cursor** and **Windsurf** * **Cline** and **Continue** * **Custom agents** built on the MCP specification ## Connect the MCP server Developer assistance needed You might need help from your development team to connect the Messaging MCP server to your AI client. Please share this guide with them. ### Step 1. Make sure you have a Pushwoosh account and an API token The Messaging MCP server authenticates with a [Pushwoosh Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token). In the Pushwoosh Control Panel, go to **Settings → API Access**, click **Generate New Token**, select **Server**, and save the token. You will add it to the client config in the next step. The token inherits your account permissions. [Limit it to the applications](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) you want the agent to send through. ### Step 2. Add the server to your AI client Use the following endpoint: ```plaintext https://manymoney-messaging.pushwoosh.com/mcp ``` * Claude Desktop Add the server to your Claude Desktop config file (`claude_desktop_config.json`): ```json { "mcpServers": { "pushwoosh-messaging": { "url": "https://manymoney-messaging.pushwoosh.com/mcp", "headers": { "Authorization": "Token YOUR_API_TOKEN" } } } } ``` Restart Claude Desktop after saving. * Cursor / Windsurf Add the server to your `.cursor/mcp.json` (or the equivalent file in Windsurf): ```json { "mcpServers": { "pushwoosh-messaging": { "url": "https://manymoney-messaging.pushwoosh.com/mcp", "headers": { "Authorization": "Token YOUR_API_TOKEN" } } } } ``` Reload the editor after saving. * Other clients Point your client at `https://manymoney-messaging.pushwoosh.com/mcp` and set the `Authorization: Token YOUR_API_TOKEN` request header. Refer to your client’s documentation for adding a remote MCP server with custom headers. ### Step 3. Try it out Open a new chat in your AI client and ask something concrete: > *“In application `XXXXX-XXXXX`, send a push to my test devices with the title ‘Hello from the agent’ and body ‘This is a test message.’”* Before you approve the agent’s action, confirm the audience and message content. If the connection works, the agent sends the message and returns a Pushwoosh message code such as `PW-12345-67890`. ## Send messages from your AI client The agent sends one message per request. Review before you approve Messages sent through the Messaging MCP server are real deliveries. They go out as soon as you approve a send-now action, or at the schedule you set. Before you approve an action in your AI client, check the application, audience, channel, message text, and schedule. Be especially careful when the target is a segment. The agent cannot cancel a send once you approve it. You can cancel a scheduled message, or one that is still being sent, from [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) in the Pushwoosh Control Panel. Notifications already delivered can’t be recalled. ### What to include in your request Describe the details below in your chat so the agent knows what to send, who should receive it, and when. Use plain language (for example, “app XXXXX-XXXXX”, “segment cart-abandonment”). | What to include | Description | | ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `application` | [Application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code) (`XXXXX-XXXXX`) | | `platforms` | [Channel IDs](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#platform-enum) to send through (push, email, SMS, etc.) | | `target` | [Segment code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment--filter-code), [segment expression](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/), or an explicit [user](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/), [hwid](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid), or [push token](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#push-token) list ([transactional targeting](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifytransactional)) | | `message_payload` | **Push:** title and body, or a [push preset](https://docs.pushwoosh.com/product/content/push-presets/), with optional sound, badge, and open action **Email:** subject, body or [template](https://docs.pushwoosh.com/product/content/email-content/), attachments **SMS and messengers:** text or approved template | | `schedule` | Send at a specific time, after a delay, or follow user timezones | | `dynamic_content_placeholders` | Values for placeholders such as `{{first_name}}` or `{{promo_code}}` | | `campaign` | [Campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) to attribute the message to | | `frequency_capping` | [Frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping) limits how often each user receives a message within a time window | ### How the agent sends a message The agent uses the [`Notify`](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/) tool to create and send one message. Supports two targeting modes: | Mode | When to use | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Segment** | Broadcast to all users matching a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) or [segment expression](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/segmentation-language/). Supports [scheduling](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message/), [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#enable-global-frequency-capping), [send rate](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/), and [control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). | | **Transactional** | Send to a specific list of [users](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/), [hwids](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#hwid), or [push tokens](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers/#push-token). Ideal for triggered or personalized messages. | #### Example prompts **Push to a segment:** > In application `XXXXX-XXXXX`, send a push to the `cart-abandonment` segment on iOS and Android. > > Title: “Still thinking it over?” Body: “Your cart is waiting, here’s 10% off.” > > Schedule for 3 PM in each user’s local timezone. Use campaign code `spring-promo`. **Email to one user:** > In application `XXXXX-XXXXX`, send an email to user `uid-123` using email template `welcome-flow-v2` with the placeholder `{{first_name}}` set to Alex. **SMS to a segment:** > In application `XXXXX-XXXXX`, send an SMS to segment `vip-users` with body “Your exclusive offer ends tonight.” Send now. **Telegram to a segment:** > In application `XXXXX-XXXXX`, send a Telegram message to segment `subscribers-monthly` with body “Hi `{{first_name}}`, your May invoice is ready — download it from your account.” Schedule it for tomorrow at 10:00 AM UTC. ## Authentication The Messaging MCP server supports three authentication methods: | Method | How to use | | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **API token** | Add `Authorization: Token YOUR_API_TOKEN` to every request. Recommended for agents and automated pipelines. | | **Session token (SSO)** | Add `Authorization: Bearer YOUR_SSO_TOKEN`. For programmatic integrations that use Pushwoosh OAuth2 SSO to act under a specific user session. Not needed for typical agent setups — use a Server API token instead. | | **OAuth 2.0 (dynamic client registration)** | For MCP clients that implement the [MCP authorization spec](https://modelcontextprotocol.io/specification/2025-06-18/basic/authorization) instead of a static header. Scope mirrors the logged-in user’s Pushwoosh SSO session (not limited to specific applications), access tokens are valid for 7 days, and refresh tokens for 30 days. See [OAuth 2.0 login flow](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/#oauth-20-login-flow) below. | Requests with a missing or invalid token are rejected with HTTP 401. ### OAuth 2.0 login flow Clients that support the MCP spec’s OAuth flow (for example, Claude Desktop’s remote-server login) don’t need a manually pasted token — the client discovers the authorization server, registers itself, and takes the user through a Pushwoosh SSO login and consent step: 1. **Discovery.** The client fetches `/.well-known/oauth-protected-resource` and `/.well-known/oauth-authorization-server` on the MCP endpoint’s host to find the `authorize`, `token`, and `register` endpoints. 2. **Dynamic client registration (DCR).** The client `POST`s to `/register` to obtain a `client_id` without any manual setup in the Control Panel. 3. **Authorization with PKCE.** The client sends the user to `/authorize` with a `code_challenge` using `code_challenge_method=S256`. PKCE is mandatory — the token endpoint does not authenticate the client by any other means, so a request without a valid S256 challenge is rejected. 4. **Pushwoosh SSO login and consent.** The user logs in through Pushwoosh SSO, then confirms the client’s access on the `/oauth/consent` screen before an authorization code is issued. 5. **Token exchange.** The client exchanges the code and PKCE verifier at `/token` for an access token, then sends it as `Authorization: Bearer` on MCP requests. You don’t need to configure any of this yourself — it only matters if you’re troubleshooting a client that implements the flow differently than expected. ## Tips and best practices * **Always review each send carefully before you approve it.** These are real deliveries, and they go out when you approve or at the scheduled time. Make sure the app, audience, channel, and message text in the client match what you intended. * **Use a dedicated Server API token for the agent.** Create a separate [Server API token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#server-api-token) in **Settings → API Access** and [limit it to the applications](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#edit-token) the agent should send from. That way the agent’s access stays scoped to just those apps. * **Use transactional targeting for triggered messages.** When you send to a known user ID after an event (order shipped, password reset), use `transactional` mode with `users: [userId]`. Do not build a one-person segment for the same send. See [NotifyTransactional](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/#notifytransactional). * **Test on test devices before a big send.** Before sending to a real segment, ask the agent to send to your [registered test devices](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/) first. For example: *“In application `XXXXX-XXXXX`, send a push to my test devices with title ‘Test’ and body ‘Check text here.’”* This routes the message only to devices you added in **Settings → Test Devices**. When the preview looks right, ask for the real segment or audience. * **Name a campaign in your request.** Include a [campaign code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#campaign-code) in chat (for example, `spring-promo`) so results appear under the right campaign in the Pushwoosh Control Panel. * **Confirm before broadcasting.** Configure your AI client to ask for explicit confirmation before approving any tool call that targets a segment. A broadcast to a large audience is irreversible. * **Use placeholders for personalization.** Pass values for placeholders like `{{first_name}}` or `{{promo_code}}` in your chat request rather than building separate messages for each user. ## Related [ManyMoney AI MCP server: plan and build campaigns ](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/) [AI Assistant inside the Control Panel ](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) [AI tools overview ](https://docs.pushwoosh.com/product/pushwoosh-ai/) --- # Troubleshoot with AI Assistant **URL:** https://docs.pushwoosh.com/product/pushwoosh-ai/troubleshoot-with-ai/ Learn how to get faster diagnosis from AI Assistant with structured prompts and the right diagnostic details for each issue type. --- This guide explains how to troubleshoot issues with [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/). When you provide structured information in your prompts, AI Assistant can diagnose faster and suggest more precise fixes. Tip If you need human support, you can [submit a ticket](https://help.pushwoosh.com/hc/en-us/requests/new) to Pushwoosh Support. ## What AI Assistant can inspect AI Assistant uses Pushwoosh APIs to inspect: | Category | What it inspects | | ----------------- | -------------------------------------------------------------------------------------------------------------------------------- | | Journey | Configuration and validation, stats by journey point, user flow for a specific user | | Message delivery | Tracking logs for delivery status and error codes, message and campaign statistics, delivery and error reports | | Users and devices | Device registration and status, user tags, [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) compilation and size | | Configuration | Application and platform settings, presets (push, email, etc.), segment and event logic | ## How AI Assistant diagnoses issues When you describe a problem, AI Assistant: 1. Gathers diagnostic information using Pushwoosh APIs and your project data. 2. Checks multiple failure points (journey configuration, presets, segments, platform settings). 3. Identifies the root cause from patterns in the data. 4. Suggests step-by-step resolution and explains why the issue occurred so you can avoid it in the future. ### What to expect from AI Assistant’s diagnosis In the response you get: * Primary issue and current state of your project * Why it is not working and fix options (with pros and cons) * Any additional issues found * For actions that change your account, AI Assistant asks for explicit confirmation before proceeding **Response time:** Typically 10–30 seconds for a full diagnosis. ## Do’s and don’ts when troubleshooting with AI Assistant Include the right details in your prompt so AI Assistant can diagnose effectively. The more accurate and complete your prompt, the quicker you get to a solution. | Do | Do not | | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------- | | **Copy exact error messages** instead of paraphrasing. | Paraphrase errors (e.g. “it said something about invalid”). | | Include **journey URLs** (copy from your browser when the journey or resource is open) so [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) and names can be taken from them. | Assume AI Assistant knows your setup (“the usual configuration”). | | Specify **timezone** when the issue involves timing or scheduling (e.g. UTC, Europe/Moscow). | Omit platform (iOS, Android, Web, etc.). | | Share **relevant dates** (e.g. “started Monday” or “since we updated on Jan 15”). | Say “it does not work” without describing expected vs actual behavior. | | Mention **what you have already tried** so repeated steps can be skipped. | Mix several unrelated issues in one prompt. | Follow up in the same conversation Some issues need several rounds. Keep the same conversation and add each new finding so AI Assistant can build on it. ## Prompt templates by issue type Use the templates below as checklists so your prompt has everything AI Assistant needs for diagnosis. To find identifiers, see [Key API identifiers](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/). ### Push notification delivery **Use when:** Push notifications are not delivered on iOS, Android, or Web. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ----------------- | ----------------------------------------------------------------------------------------------------------- | | Platform | iOS, Android, or Web | | Journey | [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) or full Control Panel URL if applicable | | When it started | Date and time, or “never worked” | | Error | Exact message, if any | | Test device | [Hardware ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#hardware-id) (HWID) if testing on a specific device | | Expected | What should happen | | Actual | What happens instead | Example prompt Push notifications for iOS are not delivering in the “Welcome Flow” journey. * Journey: `https://app.pushwoosh.com/journeys/{APP_CODE}/{STATUS}/{JOURNEY_UUID}` * Started: Yesterday at 2pm * Error: Users appear in journey stats but no push is received * Test device HWID: test-iphone-12-hwid * Expected: Push sent right after user registers * Actual: User enters the journey but no notification appears ### Email delivery **Use when:** Emails are not delivered or bounce. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ------------------- | -------------------------------------------------------------------------------------------------- | | Campaign or journey | Name or [UUID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) | | Email template | Name or [email content code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#email-content-code) | | From address | | | Domain verification | Verified, not verified, or unknown | | Who is affected | All users, a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/), or test emails only | | Bounce type | Hard, soft, complaint, or unknown | | When it started | Date or “never worked” | | Test email | if testing | Example prompt Emails are not being delivered in the “Abandoned Cart” journey. * Journey: `https://app.pushwoosh.com/journeys/{APP_CODE}/{STATUS}/{JOURNEY_UUID}` * Email template: “Cart Reminder Day 1” * From: * Domain verification: Shows “NOT READY” * Recipients: All users * Started: Today when the journey was launched ### Customer journey not working **Use when:** A journey shows no or unexpected activity. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Journey name, URL or UUID | Full URL or [Journey ID](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#journey-id) | | Status | Draft, Inactive, Active, Paused, or Archived | | Entry type | [Trigger-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/), [audience-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/), or [API-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/) | | Entry event or segment | [Event name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#event-name) or [Segment name](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#segment-filter-name) | | Expected users | Approximate number or “should have entered” | | Date range | Period you are looking at in stats | | Last change | If you changed something recently | Example prompt Customer journey shows no activity. * Journey: “Black Friday 2024 Campaign” * URL: `https://app.pushwoosh.com/journeys/{APP_CODE}/{STATUS}/{JOURNEY_UUID}` * Status: Active (started 2 days ago) * Entry type: Trigger-based * Entry event: “AddToCart” * Expected: At least 100 users should have entered * Date range: Last 7 days * Last modified: Send time was changed yesterday ### Segmentation issues **Use when:** A segment does not match the expected users. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | Segment name | Name of the segment as shown in Control Panel (e.g. “High-Value Customers”) | | Segment criteria | Description of the filter conditions | | Expected size | Approximate number of users you expect | | Actual size | Segment size as shown in Control Panel, or “error” if it fails | | Tags or events | [Tag](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#tag) and [event](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#event-name) names used in the segment | | Error message | Exact message if segment creation or compilation failed | | Use case | What you are trying to target (e.g. VIP offer, reactivation) | Example prompt Segment does not match expected users. * Segment: “High-Value Customers” * Filter: Users with “Total Purchases” greater than $500 in the last 90 days * Expected: About 1,000 users * Actual: 0 users * Tags used: “Total Purchases” (should be PRICE type) * Error: Filter compiles but returns no matches * Use case: Targeting VIP customers for an exclusive offer ### Integration and SDK issues **Use when:** Registration, tags, events, or push receipt fails in your app. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ----------------- | -------------------------------------------------- | | Platform | iOS, Android, Web, React Native, or Flutter | | SDK version | Version number | | Framework | Swift, Objective-C, Kotlin, Java, JavaScript, etc. | | Issue | Specific problem | | What works | Steps that succeed | | What fails | Steps that fail | | Error logs | Full error text | | Code snippet | Relevant code, if useful | | Tested on | Device, emulator, or production | Example prompt SDK integration issue on iOS. * Platform: iOS 16+ * SDK version: Pushwoosh 6.5.2 * Framework: Swift * Issue: Device registers but tags are not saved * Works: Push token registration, receiving notifications * Fails: `sendTags()` calls appear to be ignored * Error logs: None, but tags do not appear in Control Panel * Code: `pushwoosh.sendTags(["userId": "12345", "premium": true])` * Tested on: iPhone 14 Pro simulator and physical device ### Analytics and reporting **Use when:** You need to understand a metric or report. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ----------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | Metric or report | Name of the metric or report (e.g. Delivery rate, Journey exits) | | Time period | Date range (e.g. Last 7 days, January 2024) | | Filters | [Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/), or platform | | Current value | Value or message you see in Control Panel | | Expected | What you expected or “need to understand” | | Decision impact | How you will use this data | ### Performance and speed **Use when:** An operation is slow or times out. Include these details in your prompt to AI Assistant: | Include in prompt | Example or format | | ----------------- | ----------------------------------------------- | | Operation | What is slow (e.g. segment compilation) | | When | Always, at peak times, or for a specific action | | Volume | How many users or messages are affected | | Timing | How long it takes vs what you expect | | Impact | Business impact | | Recent changes | Anything changed before the issue started | ## Proactive checks You can ask AI Assistant for a proactive audit. Examples of prompts you can use: * **Journey:** “Audit my journey configuration for potential issues. Journey: `name or UUID`. Focus: `delivery`, `performance`, `user experience`, or `all`.” * **Email:** “Check my email setup for deliverability issues. Application: [application code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#application-code). I send from: `your from address, e.g. noreply@mystore.com`. Lately: `e.g. bounces in the last 2 weeks, or no issues`.” * **Campaign:** “Analyze campaign performance and suggest improvements. Journey or campaign: `name or UUID`. Current metrics: `what you see`. Goals: `what you want to achieve`.” --- # Introduction to Knowledge hub **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/ Learn key Pushwoosh concepts, from device identifiers to omnichannel campaigns and data collection methods in the platform. --- The Pushwoosh Knowledge Hub provides essential information about key concepts related to user identification, device management, and campaign strategies. This section covers the following topics: [Device identifiers ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/) [Learn about unique device identifiers in Pushwoosh](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/) [Types of registered devices ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/) [Understand the different types of devices](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/) [Push Permission Status tag ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) [Segment users by push permission state](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) [Users (UserIDs) ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) [Discover how UserIDs work in Pushwoosh](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) [Omnichannel campaigns ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) [Discover how to set up and manage omnichannel campaigns](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/) [Data collected by Pushwoosh ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/what-data-does-pushwoosh-collect/) [Get insights into the types of data Pushwoosh collects](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/what-data-does-pushwoosh-collect/) [Security certifications and compliance ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/security-certifications/) [Learn about Pushwoosh's security certifications and compliance standards](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/security-certifications/) Tip For developer-focused implementation details on device identifiers, device types, and User IDs, see the [Developer Knowledge hub](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/). --- # Device identifiers **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/ Identify devices with HWIDs and push tokens to enable precise targeting and higher push delivery rates. Learn how to collect IDs from your user base. --- There are two ways to identify a particular device registered with Pushwoosh – a hardware ID (HWID) and a push token. ## HWID HWID (or Hardware ID) is a key identifier assigned to the device by Pushwoosh SDK. #### Android HWID For Android devices, HWID is a random set of letters and digits generated by the first app with the Pushwoosh SDK installed on the device. It’s later shared with other apps that contain Pushwoosh SDK so that all such apps will have the same HWID as a result. However, if a user deletes all the apps with Pushwoosh SDK and then re-installs some, a new HWID will be generated once again. Android HWID example: `5dcaed99-e1e0-44dc-bde2-b96188b99e9e` #### iOS HWID For iOS devices, IDFV ([Identifier for Vendor](https://developer.apple.com/documentation/uikit/uidevice/1620059-identifierforvendor)) is being used as a HWID of the device. It won’t change as long as there is at least one app from the same vendor installed on the device. iOS HWID example (upper case only!): `CBAF8ED1-17FB-49A3-73BD-DC79B63AEF93` ## Push token Push token (device token) is a unique key for the app-device combination issued by the Apple or Google push notification gateways. It allows gateways and push notification providers to route messages and ensure the notification is delivered to the unique app-device combination for which it is intended. #### iOS push token iOS device push tokens are strings with 64 hexadecimal symbols. iOS push token example: `03df25c845d460bcdad7802d2vf6fc1dfde97283bf75cc993eb6dca835ea2e2f` Caution Make sure that the iOS push tokens you use when targeting specific devices in your API requests are in **lower case**. #### Android push token Android device push tokens can differ in length (usually below 255 characters) and typically start with APA91b…. However, it might also have a colon-separated prefix. Android push token example: `APA91bFoi3lMMre9G3XzR1LrF4ZT82_15MEICogXSLB8-MrdkRuRQFwNI5u8Dh0cI90ABD3BOKnxkEla8cGdisbDHl5cVIkZah5QUhSAxzx4Roa7b4xy9tvx9iNSYw-eXBYYd8k1XKf8Q_Qq1X9-x-U-Y79vdPq` Caution Using raw device tokens for targeting specific devices is not the most reliable way because FCM push tokens tend to change from time to time, and it’s hard to tell how often it occurs. Important To obtain the HWID and push token, you’ll need assistance from your development team. Please share [this link](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/device-identifiers#how-to-obtain-hwid-and-push-token) with them for detailed instructions. --- # Omnichannel campaigns **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/ Learn how to create omnichannel campaigns and unify communications across push, email, SMS, in-app, and WhatsApp channels. --- Pushwoosh enables you to create omnichannel campaigns and seamlessly unify your communications across different channels such as push notifications, emails, SMS, in-apps and WhatsApp. This guide explains how omnichannel campaigns work in Pushwoosh, and how to set up integration to create these campaigns. ## Key identifiers for omnichannel campaigns in Pushwoosh To effectively manage omnichannel campaigns, it’s essential to understand how Pushwoosh identifies users across various devices and channels. Pushwoosh uses three primary identifiers by default: * **Device ID (HWID)**: A unique identifier assigned to each device by the Pushwoosh SDK. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers#hwid) * **User ID**: A unique identifier that represents the user of the device. By default, the User ID is equal to the HWID. However, the User ID is reserved for you, allowing you to later replace the default User ID with a custom one. This enables mapping users across different systems or linking different devices and communication channels under one User ID for omnichannel campaigns. [Learn more about User IDs](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids). Developer assistance needed You’ll need assistance from your development team to set a custom User ID. Please share [this guide](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/collecting-user-ids) with them to get started. * **Push token**: A unique key for the app-device combination, issued by Apple or Google push notification gateways. It is collected and managed by the Pushwoosh SDK. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers#push-token) ## Understanding how Pushwoosh handles devices In Pushwoosh, any contact method — such as a [device identifier (HWID)](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers), email address, or phone number — is treated as a separate entity or “device”. Each “device” is stored as a distinct entry in the Pushwoosh database. For instance: * **HWID** for mobile or web push notifications * **Email addresses** for email communications. * **Phone numbers** for SMS messages and WhatsApp communications For example, if a user has both a mobile device and an email address, Pushwoosh counts the device identifier (HWID) and the email address as two separate entities (devices). As a result, there will be two separate entries in the Pushwoosh database. [Learn how to register devices in Pushwoosh](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/) ## How to link devices for effective omnichannel campaigns To run omnichannel campaigns in Pushwoosh, where you can reach the same user via push notifications, email, SMS, or WhatsApp, you must link all of a user’s contact methods under a single, unique User ID. This essential process ensures Pushwoosh recognizes that separate contact points (like a phone, email, or SMS number) all belong to the same user. Pushwoosh allows a single User ID to be associated with multiple devices and channels. This is the foundation that ensures all user actions and contact points are treated as belonging to the same unified profile. ##### What you need to do 1. Set a custom, unique User ID that will connect all the channels/devices related to the same person (for example, “alex_smith_123”). 2. When registering each contact method (mobile device, email address, phone number/SMS, or WhatsApp) in Pushwoosh, assign the same User ID to all of them. This means when you register a device with the Pushwoosh SDK or API, or import users via CSV, always use the same User ID for each point of contact belonging to the user. Note When you register devices with the same User ID, Pushwoosh automatically links them in your user base. There’s no extra configuration needed, but you must ensure the **same User ID** is provided for each contact method you register. ##### Why is this important? If you don’t link devices under a single User ID, each device or contact method will be treated as an independent user, and you won’t be able to target people across channels or coordinate messaging. ##### Example Suppose you want to send a follow-up email or SMS to users who did not open a push notification. To do this seamlessly, you must ensure that all of the user’s contact details (such as their mobile device, email address, and phone number) are associated with the same User ID. This way, Pushwoosh can recognize and reach the user across channels as a single profile. Here’s a simplified representation of how this might look in the Pushwoosh database: | Platform | Device (HWID) | User ID | | -------- | ------------------ | ------- | | Mobile | abc123pushToken | user123 | | Email | | user123 | | SMS | +1234567890 | user123 | | WhatsApp | +1234567890 | user123 | In this example, the custom UserID **“user123”** is linked to four different devices: a mobile device, an email address, an SMS number, and a WhatsApp contact. This means Pushwoosh recognizes that all these devices belong to the same user, allowing you to contact them through different channels. Tip To see all the devices linked under a User ID, type the User ID into the [Pushwoosh User Explorer](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/). You’ll be able to view all active user devices associated with that User ID, as well as all [user tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/). ![User Explorer showing multiple devices linked to a single User ID with device types and tags](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-omnichannel-campaigns-1.webp) ## Example scenario Developer assistance needed Configuring the steps outlined below requires assistance from your development team. Please share this guide with them for proper implementation. Imagine you manage an eCommerce app and want to reach your customers through various channels, including push notifications, emails, SMS, and WhatsApp, ensuring consistent and personalized messaging across all platforms. Here’s how it might look: ### Step 1. Registering the device and setting a custom User ID When a new customer, let’s call her Sarah, downloads your app, the Pushwoosh SDK automatically registers her device in Pushwoosh using its Hardware ID (HWID) as the default User ID. After Sarah creates an account and logs in, you assign her a custom User ID, `sarah_123`, using the [setUserId](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/collecting-user-ids#setuserid-sdk-method) SDK method. ### Step 2. Expanding to email Next, encourage Sarah to sign up to receive promotional emails and register her email address in the Pushwoosh user base using the [registerEmail](https://docs.pushwoosh.com/developer/api-reference/email-api#registeremail) API method. You can also register an email address via corresponding methods using either Pushwoosh [mobile SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk) or [Web SDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30) if you need to do so from your mobile application or website. Set the custom User ID to `sarah_123` and you can also include any tags that should be associated with the email record, like her preferences or activity. ### Step 3. Expanding to SMS Similarly, when Sarah agrees to receive SMS messages and provides her phone number, you should use the [registerDevice](https://docs.pushwoosh.com/developer/api-reference/device-api#registering-sms-devices) API method to link her number **(+1234567890)** with her User ID **sarah_123**. Sarah’s mobile device, email, and phone number are now linked to her User ID, **sarah_123**. This setup enables you to send [personalized messages](https://docs.pushwoosh.com/product/personalization/dynamic-content) to Sarah through [push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/), [emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails), and [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/) channels, ensuring a seamless and consistent user experience. ## How omnichannel works in customer journeys * When setting up omnichannel customer journeys, make sure to enable the [Send to all user devices](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#send-by-user-id) option. This ensures the message is delivered to the appropriate channel across all user devices. ![Send to all user devices toggle enabled in Customer Journey message element settings](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-omnichannel-campaigns-2.webp) * To make omni-channel campaigns work effectively, users who enter a journey should have all the channels used in the journey. For example, if you start a campaign with a push notification, only customers with registered mobile devices will receive it. If they don’t have a mobile device, they won’t be able to continue. The same goes for email campaigns. --- # Push Permission Status tag **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/ Segment users by push permission state, from never asked and provisional to granted or denied, on iOS, Android, and Web. --- **Push Permission Status** is a [default tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) that stores the state of the push notification permission on a device. Use it to build [reachability segments](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/#build-reachability-segments) of users that a simple on/off flag can’t separate, for example users you can still ask for permission and users who already declined. Note You can’t set this tag yourself, because the Pushwoosh SDK collects it automatically on iOS, Android, and Web. A value you send with [`setTags`](https://docs.pushwoosh.com/developer/api-reference/device-api/#settags) is ignored, and you can’t create a [custom tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#custom-tags) with this name. ## Push Permission Status values The tag is a String tag that stores one of the values below for each device in each application. Each platform reports only some of them. See [What each platform reports](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/#what-each-platform-reports). | Value | What it means | | ------------------ | ----------------------------------------------------------------------------------------------------------------------------- | | `not_determined` | The user hasn’t answered the permission prompt yet, so the app can still ask | | `granted_explicit` | The user allowed notifications in the permission prompt | | `granted_default` | Notifications are allowed, but not through an answer to the Pushwoosh SDK permission prompt. Android only | | `provisional` | Notifications are allowed provisionally. They go quietly to Notification Center, without alerts, sounds, or banners. iOS only | | `ephemeral` | Notifications are allowed for a limited time, as for an [App Clip](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-app-clips/). iOS only | | `denied` | The user turned notifications off, either in the prompt or later in device or browser settings | | `unsupported` | The browser can’t receive web push notifications. Web only | The tag has no value on tvOS and macOS, and on devices with an SDK version older than: | Platform | Minimum SDK version | | -------- | ------------------- | | iOS | 7.2.10 | | Android | 6.17.0 | | Web | 3.95.0 | ## When the tag updates The SDK reports the current status at these moments: * Every time the app opens. * When the device registers for push notifications. * Right after the user answers the permission prompt. If the user changes the permission in device or browser settings, the tag updates later, depending on the platform: | Platform | When the tag updates | | ---------------------------------------- | -------------------------------------------------------- | | iOS | The next time the app opens | | Android | The next time the app opens or returns to the foreground | | Web: Chromium-based browsers and Firefox | As soon as the browser reports the change | | Web: Safari | On the next page load | When a request comes without a status, for example from an older SDK version, the tag keeps its last value. ## What each platform reports Each platform reads the permission from its own system API, so the set of values differs by platform. ### iOS The iOS SDK maps the system authorization status of the app directly to the tag. | iOS authorization status | Push Permission Status | | ------------------------ | ---------------------- | | Not determined | `not_determined` | | Authorized | `granted_explicit` | | Provisional | `provisional` | | Ephemeral | `ephemeral` | | Denied | `denied` | ### Android Android 13 and later require the user to allow notifications in a permission prompt. Earlier versions allow them by default after installation, so the Android SDK separates an explicit answer from a default grant: * `granted_explicit`: the user allowed notifications in the permission prompt that the Pushwoosh SDK shows, on Android 13 or later, in an app that targets Android 13 (API level 33) or later. * `granted_default`: notifications are allowed in any other way. That includes Android 12 and earlier, apps that target an API level below 33, a permission that was already allowed when the SDK first checked it, and a permission allowed from your own prompt or from system settings. * `not_determined`: on Android 13 and later, notifications are off and the user hasn’t answered the prompt yet, including when they closed the prompt without choosing. * `denied`: notifications are off after the user declined the prompt, or after they were allowed before. On Android 12 and earlier, any device with notifications turned off reports `denied`. ### Web Browsers allow notifications only after the user clicks to allow them, so a web device never reports `granted_default`. | Browser permission | Push Permission Status | | -------------------------------- | ---------------------- | | Not asked yet (`default`) | `not_determined` | | Allowed (`granted`) | `granted_explicit` | | Blocked (`denied`) | `denied` | | Browser doesn’t support web push | `unsupported` | ## Build reachability segments Combine the values in one **Push Permission Status** filter with the **in** operator to target users by how you can reach them. [Learn more about building segments by tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#string-tag-operators). | Segment | Filter | How to use it | | ------------------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Users who see push alerts | **in** `granted_explicit`, `granted_default` | Send time-critical push notifications | | Quiet delivery only | **in** `provisional`, `ephemeral` | Expect lower visibility, and invite these users to allow full notifications | | Users you can still ask | **in** `not_determined` | [Show an in-app message before the push permission prompt](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/) | | Users who declined | **in** `denied` | Reach them through email, SMS, or in-app messages, or [run an opt-out recovery popup](https://docs.pushwoosh.com/product/content/in-apps/create-opt-in-and-opt-out-recovery-popups-in-pushwoosh-in-app-editor/#opt-out-recovery-popup) | | Browsers without web push | **in** `unsupported` | Use other channels for these web users | To find devices that don’t report the tag yet, use the **not set** operator. These devices run an older SDK version, so target them with [Push Alerts Enabled](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) instead. For the difference between the two tags, see [Push Permission Status vs Push Alerts Enabled](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/#push-permission-status-vs-push-alerts-enabled). ## Push Permission Status vs Push Alerts Enabled The two tags answer different questions: * [**Push Alerts Enabled**](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) (Boolean): can this device show push alerts right now? * **Push Permission Status** (String): what is the state of the permission? It separates states that **Push Alerts Enabled** reports as the same `false`, such as never asked and declined. The table compares how the two tags report common states. | | Push Alerts Enabled | Push Permission Status | | ------------------------------------------ | ----------------------------------------------- | --------------------------------------- | | Type | Boolean | String | | Never asked for permission | `false` | `not_determined` | | Declined permission | `false` | `denied` | | Provisional or temporary permission on iOS | Depends on the alert, sound, and badge settings | `provisional` or `ephemeral` | | Explicit or default grant | `true` when the device shows alerts | `granted_explicit` or `granted_default` | | Devices with older SDK versions | Reported on all SDK versions | Not set | Choose the tag by the question you need to answer: * Use **Push Alerts Enabled** for a quick opted-in count across all SDK versions. * Use **Push Permission Status** when your next step depends on why a user isn’t reachable. For example, ask the users who were never asked and leave alone the users who declined. --- # Security certifications and compliance **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/security-certifications/ Pushwoosh compliance standards including ISO 27001 (2022), SOC 2 Type 2, GDPR, HIPAA, OWASP principles, and the EU-U.S. DPF. --- Pushwoosh maintains security and data privacy practices aligned with recognized compliance standards. For an overview of how Pushwoosh approaches data protection, see [Data safety](https://www.pushwoosh.com/products/data-safety/). ## Which compliance standards does Pushwoosh follow? Pushwoosh follows the compliance standards listed below. Use this table for a quick view of status and scope. For standards with formal certificates or audit reports, see the next section. | Compliance standard | Compliant | Details | | ------------------------------------ | --------- | ------------------------------------------------------------------------------------------------------------------------- | | ISO 27001:2022 | Yes | Pushwoosh meets industry standards for online security and is certified by ISO 27001:2022. | | GDPR | Yes | Pushwoosh maintains the highest standards of compliance with data protection regulations. | | OWASP principles | Yes | The Pushwoosh platform embraces the Security by Design approach to prevent potential threats. | | HIPAA | Yes | Pushwoosh is HIPAA compliant, ensuring the confidentiality, integrity, and availability of PHI (ePHI). | | SOC 2 Type 2 | Yes | Pushwoosh possesses a SOC 2 Type 2 report, reflecting its commitment to ensuring the utmost security of user data. | | EU-U.S. Data Privacy Framework (DPF) | Yes | Pushwoosh is officially certified under the EU-U.S. Data Privacy Framework, including the UK extension and the Swiss DPF. | ## Certificates and reports The details below explain how each standard was verified and where to find supporting information. ### ISO 27001:2022 Covers Pushwoosh’s information security management system (ISMS). Confirmed by Americo, a globally recognized ISO certification body. [Learn more](https://www.pushwoosh.com/blog/iso-27001-certification/). ### SOC 2 Type 2 Report based on an independent auditor’s assessment of controls over an observation period, confirming they operated effectively rather than only being suitably designed. The SOC 2 framework is built around the AICPA Trust Services Criteria, including security, availability, processing integrity, confidentiality, and privacy. [Learn more](https://www.pushwoosh.com/blog/soc-2-type-2/). ### HIPAA Confirmed by an independent gap assessment conducted by Riskpro India. Pushwoosh enters into Business Associate Agreements (BAA) with covered entities. [Learn more](https://www.pushwoosh.com/blog/hipaa-compliance/). ### EU-U.S. Data Privacy Framework (DPF) Pushwoosh, Inc. has certified to the U.S. Department of Commerce that it adheres to the EU-U.S. DPF, the UK extension, and the Swiss-U.S. DPF. [Learn more](https://www.pushwoosh.com/data-privacy-frameworks/). --- # Types of registered devices **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/ Understand how Pushwoosh tracks device types to improve push delivery, analyze opt-in rates, and boost conversions. --- There are three types of records about devices registered with Pushwoosh: * [Total Devices](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#total-devices) * [Push Enabled devices](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#push-enabled-devices) * [Push Alerts Enabled devices](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) ## Total Devices **Total Devices** number is indicated in your Pushwoosh account — the right-hand number on the top left of the Control Panel. **Total Devices** include **all devices that were registered** with Pushwoosh and were **active within the last 90 days**. They may or may not have push tokens. Devices with no push tokens are reachable with In-App Messages. Note Push token is a unique key that identifies an app on a specific device; it is issued by cloud gateways and used to deliver push notifications to the device. The device is registered once a user opens the app with the Pushwoosh SDK for the first time after installation. Once the app is launched, the SDK will make several calls to our backend, such as `/applicationOpen` and `/getInApps`. This will record a device with its HWID (Hardware ID) to the Pushwoosh database and make it eligible for receiving In-App Messages, and the app open stats will be increased by one. Once the device is recorded to a database, you can start submitting Tags for that device and assign a User ID, if necessary. At this point, the push token does not exist yet, and this device will fall under the Total Devices category. If a device is not active for more than 90 days, e.g., there are no `/applicationOpen` requests from our SDK for that device, no Events triggered, and that device does not have a push token, it will be erased from the database. This period of inactivity is configurable; to reset the inactivity period, apply to your Customer Success Manager or our Customer Support team. ## Push Enabled devices Push Enabled devices are devices with push tokens associated. That means you can send push notifications to such devices, and cloud gateways will deliver your pushes. Push Enabled devices number is indicated in your Pushwoosh account — the left-hand number on the top left of the Control Panel. As soon as the user agrees to receive push notifications, the push token is issued by cloud gateways, such as APNS and FCM, and our SDK sends an API request to [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) endpoint of Pushwoosh with this token. Now, since we have a push token registered in our database for a device, it is considered Push Enabled, and you can start sending push notifications to that device. Note In practice, even if there is a push token associated with a device, some users may not see push notifications in case they disabled alerts manually from their device settings. Those users are reachable with Background (silent) pushes, In-App Messages, and can receive notifications to the Inbox. See [Push Alerts Enabled](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/types-of-registered-devices/#push-alerts-enabled-devices) for more info. When you’re sending a push notification, the APNS and FCM return us the status of each recipient’s push token. If any token is marked invalid or nonexistent, we will remove it from the database, and the number of Push Enabled devices will decrease accordingly. When the push token is removed from the system, the device is no longer in the Push Enabled category but remains in Total Devices. If a user opens the app after that, the device will be assigned a new push token and go back to Push Enabled devices. If there is no activity within 90 days after removing the token, the device is totally erased from the database. ## Push Alerts Enabled devices Push Alerts Enabled devices can display push notification alerts outside the app: on the lock screen, in the notification center, or as a badge on the app icon. It depends on whether a user enables or disables push notifications alerts from their device settings. Devices with alerts enabled are marked with the “True” value of the Push Alerts Enabled default tag, while the rest are marked “False.” You can check this in your Tags’ statistics. This Tag tracks how many users can actually receive push notification alerts when they have the app in the background or closed. Push Alerts Enabled tags are default tags and are collected automatically by Pushwoosh SDK. Latest versions of Pushwoosh SDK (released Fall 2018 or later) are required. Important The alerts status can only be updated in Pushwoosh if the user opened the app at least once after changing the notification preferences in device settings. In case the user allows push notification alerts but doesn’t open the app, we won’t recognize such a user as a Push Alerts Enabled one; that’s why it’s so important that you target all devices with tokens to ensure maximum reach. Comparing Push Enabled and Push Alerts Enabled devices gives you a better picture of your opt-in rates and can help to choose the right communication channel to engage with the broader audience. Building a segment of devices with disabled alerts allows you to reach them via alternative channels and motivate them to re-enable the notification alerts. To see why a device can or can’t show alerts, for example whether the user was never asked or declined, use the [Push Permission Status](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) tag. --- # Users (User IDs) **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/ Link users to multiple devices with User IDs in Pushwoosh for consistent, personalized messaging across all platforms. --- User ID is a unique identifier that is assigned to each user to maintain consistent user data across all their devices. This allows you to provide your audience with a seamless user experience across their devices. ## How are users and devices related? Each user is assigned a unique identifier called User ID. User IDs can be linked to multiple devices, allowing businesses to stay connected with users even when they switch devices. This ensures that targeted messages reach the intended user on whichever device they are using. A user (User ID) is not a standalone entity; it relies on the presence of associated devices. If the last associated device is removed or linked to another user, the user will be automatically deleted from the system. A user (User ID) also functions as an entity under which several devices can be grouped, each with its own unique Hardware ID (HWID). ![Diagram showing relationship between User IDs and multiple devices with unique HWIDs](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-users-userids-1.webp) Note Pushwoosh supports [linking multiple devices to a single User ID](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/) without any additional configuration. Simply register each device with the same User ID, and they will be automatically grouped under one user profile. For step-by-step instructions on how to do this, see [Set a custom User ID and link it to multiple devices](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/). ### Device limit per user Each user can have a maximum of 20 devices linked to their User ID. When a 21st device is registered, the system automatically removes one of the existing devices to make room. **How the device for removal is selected** 1. If all linked devices are email-only, a random email device is removed. 2. Otherwise, the system finds the oldest non-email device based on the last app open time and removes it. 3. If there’s no activity data to compare (e.g., devices were added via API without actual app usage), a random non-email device is removed. This ensures that your most recently active devices stay linked to your account while older, inactive devices are automatically cleaned up. ## Types of users There are two types of users: anonymous and known users. An anonymous user has their User ID equal to the device HWID, which can be replaced with the actual User ID later on. A known user has a distinct User ID. You can specify a User ID when registering a new device through the [`/registerDevice`](https://docs.pushwoosh.com/developer/api-reference/device-api/#registerdevice) API call. ## Obtaining and managing User IDs Pushwoosh provides several ways to access and manage Users: ### Creating users By default, when an app with the integrated Pushwoosh SDK is launched for the first time, it sets a device HWID as a User ID. You can call the [`setUserID`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/#setuserid-sdk-method) method from the Pushwoosh SDK on login to set any required value to associate a device with a particular user. When a user logs out, you can reset this value to a default one with another [`setUserID`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/#setuserid-sdk-method) call, e.g. to an initial HWID value. ### Importing users You can easily import User IDs from a CSV file for a specific user segment. Please find our detailed instructions in the [Segments guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import). ### Exporting users To export User IDs, you can utilize the [`/exportSegment`](https://docs.pushwoosh.com/developer/api-reference/segmentation-filters-api/#exportsegment) API request, which provides the option to export data in CSV or JSON format. ### Accessing User IDs from device logs User IDs can also be directly accessed from device logs. For step-by-step instructions, please refer to the article [How to Obtain Console Logs and Device Tokens](https://help.pushwoosh.com/hc/en-us/articles/360000292586-How-to-obtain-console-logs-and-device-token?source=search&auth_token=eyJhbGciOiJIUzI1NiJ9.eyJhY2NvdW50X2lkIjoyOTA5MjksInVzZXJfaWQiOjEyNDkzMTI5MDkwMzMzLCJ0aWNrZXRfaWQiOjc4MzMyLCJjaGFubmVsX2lkIjo2MywidHlwZSI6IlNFQVJDSCIsImV4cCI6MTY5MzExNDk5Mn0.SpeS-IJ_j3mB6Elf2ObMjev2MZSmDOLu4DYQdEcZvS8). ## User and device tags Just like devices, users (User IDs) can have their own sets of [tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) that will serve as labels to categorize and identify users or devices based on their characteristics. ### How to create user-specific tags When you set a user-specific tag, it automatically applies to both users and all their associated devices. This means the tag will be linked to the user’s profile and will be reflected on all their devices. To create a user-specific tag from the Pushwoosh Control Panel, check the **User-specific** checkbox when adding a new tag. If you don’t mark the tag as user-specific, it will be associated with the device only. ![User-specific checkbox option when creating a new tag in Pushwoosh Control Panel](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-users-userids-2.webp) ## Managing user transitions Within Pushwoosh, managing user transitions involves two primary scenarios: ### Transition from Anonymous to Known user When an anonymous user becomes a known user, the device they are using becomes associated with their new user profile. During this transition, all device attributes remain intact. However, it’s important to note that all user-specific attributes are reset to known user values or default if it does not exist. For instance, let’s consider a scenario where a guest user has been using a fitness tracking application with default settings. When they create an account, their device data remains unchanged. Still, any user-specific details, like their name, email, and customized goals, will be reset to known user values. ### Transition from one Known user to another If the User associated with a device is changed using [`setUserID`](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/#setuserid-sdk-method) from Pushwoosh SDK, the device is associated with the new user profile. > Learn more about setting User IDs for [Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-faq#how-do-i-set-a-userid) and [iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-faq#how-do-i-set-a-userid). You can also use the [**`/registerUser`**](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#registeruser) API call. Just like in the previous scenario, the device attributes remain the same, ensuring a smooth transition. However, the user-specific tags will now be assigned to the values of the new user. This is to ensure that the user’s tags reflect the new user’s profile accurately. As an example, consider a scenario in a video streaming service. If a registered user logs out and a different registered user logs in on the same device, the device settings, such as preferred language and video quality settings, are retained. Yet, any user-specific information, such as viewing history and recommendations, will be changed for the new user. ## API methods for managing users Pushwoosh provides several API methods to manage users within your application. You’ll need assistance from your development team to work with these methods. For more details, share [this link](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/#api-methods-for-managing-users) with your development team. ## If a user is associated with multiple devices, which one will receive the message during a Customer Journey? Caution By default, a user cannot be in the same journey twice at the same time. If a user triggers the entry conditions while already participating, they will not be re-entered until they complete or exit the current journey. Customer Journeys operate on User IDs. If a User ID is linked to multiple devices or contacts, none of them can re-enter the journey while the user is already in it. If you want users to enter the same journey multiple times, for example, for repeat purchases or bookings, enable the [multiple sessions setting](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-how-many-sessions-a-user-can-have-at-the-same-time) and define a session identifier (like `order_id`or `appointment_id`). When a user has multiple devices associated with their account, how messages are delivered across them depends on the journey type: ### Audience-based entry and Webhook-based entry journeys By default, in these journeys, messages are sent to the most recently active device associated with the user. This ensures the message reaches the user who is most likely to engage with it. ### Trigger-based entry journeys By default, the message is sent to the device where the triggering action was last fired. This maintains context and relevance by delivering the message on the device where the relevant user activity occurred. ### All devices delivery However, you can choose to override these default settings and send the message to all devices associated with the User ID simultaneously. For this, in the message settings, scroll down to **Options**, and toggle on **Send to all user devices**. ![Send to all user devices toggle option in message settings under Options section](https://docs.pushwoosh.com/pushwoosh-knowledge-hub-users-userids-3.webp) This option might be useful for broadcasts or urgent notifications that need to reach the user regardless of which device they’re currently using. --- # Data collected by Pushwoosh **URL:** https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/what-data-does-pushwoosh-collect/ Learn what data Pushwoosh collects, how it stores collected data, and how it complies with GDPR and privacy regulations. --- To send push notifications, in-apps, emails, etc. correctly and enable the whole set of its features, Pushwoosh requires device and user data. Segmentation based on user data is also necessary to enhance various metrics and achieve business goals. That is why Pushwoosh securely accesses this data with client control. Important It’s important to note that Pushwoosh can only access the data for which a particular customer has given permission. When integrating Pushwoosh SDK into an application, users can choose to allow or forbid the SDK to collect specific information about the app users’ devices. > In addition, certain types of data (such as precise geolocation data) are only accessible with an individual user’s explicit permission. This ensures transparency and prevents unauthorized third-party data collection. ## What data does Pushwoosh collect There are three types of data Pushwoosh collects. ### Device data collected by Pushwoosh SDK Device data is generated by a user’s device and is anonymized, preventing identification of individual users. This data is then transmitted to Pushwoosh servers for further processing, in accordance with the needs of Pushwoosh customers. Pushwoosh needs this data to correctly send push notifications and enable certain features. While device data is collected by default, app developers can manually prohibit the collection of specific data types during SDK integration. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk) See the full list of device data collected below. | Data type | Description | Comment | | -------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Geolocation information** | City and country based on the latest IP address a device was online from. | Pushwoosh uses GeoIP to determine a user’s city and country and then stores this data in the database. This does not reveal the user’s precise location and does not require the user’s permission. | | **Precise location data** | Collected if location tracking is implemented and allowed by the user (Geofencing). | | | **iOS IDFV** | Identifier for the vendor, used as device HWID for routing pushes between iOS apps of the same vendor. | Pushwoosh collects IDFV to distinguish devices in its database. This allows recognizing applications installed on one device and creating app groups with Pushwoosh’s (legacy) Application Groups feature. It allows you to send one push notification to several applications with one click, and those users who have two or more apps installed will receive only one message. | | **Device model** | The specific model of the device. | | | **Device language** | The system language of the device. | | | **OS version** | The operating system version. | | | **Application version** | The version of the app installed on the device. | | | **Device time zone** | The time zone set on the device. | | | **iOS Bundle Identifier and Android Package Name** | Used to check for correct push notification setup. | | | **Browser** | Collected for device platform recognition and as default tag values for further targeting. | | | **Other data** | Any additional data points collected by the SDK on behalf of the customer. | | **Note**: GeoIP is used for geotargeting but is not collected on Pushwoosh’s side. ### User data gathered and shared by the customer User data can include things like user demographics, preferences, etc. Pushwoosh clients have full control over what user data is shared with Pushwoosh. Pushwoosh accesses user data **only if** the app developer collects it and chooses to share it, for example, using the **setTags** method. User data enables greater personalization in marketing communications. For example, you can segment users by age group or preferences to tailor your messaging more effectively. ### Data on triggered Events This data includes various actions users perform in the app. Developers use the **postEvent** method in the Pushwoosh SDK to report these events and their attributes. Pushwoosh uses this data to create dynamic content for push notifications, enhancing personalization and relevance, which boosts user engagement and conversions. For example, you can mention the level a game player has achieved, the exact product a customer has added to the cart, or an in-app achievement a user has unlocked. ## What data does Pushwoosh not collect ### Sensitive data Pushwoosh does not collect sensitive or personally identifiable information (PII) such as email addresses, phone numbers, names, credit card details, or financial information unless provided directly by the Pushwoosh client, at the client’s responsibility. Pushwoosh does not share, distribute, market, or advertise to third parties or communicate with end-users directly. ### Other data Pushwoosh does not collect cookies, International Mobile Equipment Identity number (IMEI), or Media Access Control (MAC) addresses since they are not required for Pushwoosh functionalities. ## How long does Pushwoosh store data | Metric | Storage Period | Comment | | -------------------- | ----------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Device data | As long as the device has a valid push token | | | Inactive device data | 90 days (if no app opening event occurs within this period) | Devices that have been inactive (app not opened) for more than 90 days will be automatically removed from the system, regardless of token validity. This ensures up-to-date device data for a smoother experience. | | User data | As long as the device has a valid push token | | | Events history | 1 year | You can extend the events storage period upon agreement. For this, please [contact our support team](https://help.pushwoosh.com/hc/en-us/requests/new). | Note: You can prohibit the storage of your data after your contract with Pushwoosh ends. We can delete the data immediately on the contract expiration date or agree on a specific period after which the data will be deleted. ## Where is the data stored Pushwoosh services are hosted in Nuremberg, Germany. Data is stored in our data center located there. ## Does Pushwoosh handle data in compliance with GDPR and other regulations When using Pushwoosh, you can be assured that your data is handled in compliance with GDPR and other relevant regulations. Pushwoosh is committed to maintaining high standards of security and data privacy through various compliance measures. These include ISO 27001:2022, SOC 2 Type 2, GDPR, HIPAA, OWASP, and the EU-U.S. Data Privacy Framework. [Learn more ](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/security-certifications/) ## Is Pushwoosh compliant and does it process customer data securely Yes, Pushwoosh is compliant with various data protection regulations and ensures secure data processing. We only access the data required to create relevant messaging campaigns, and you have full control over what data is shared with us. For any concerns about data safety and security, you can always [contact our Support team](https://help.pushwoosh.com/hc/en-us/requests/new/). --- # Release notes **URL:** https://docs.pushwoosh.com/product/release-notes/ Keep up with the latest updates in Pushwoosh. New features, improvements, deprecations, and release highlights in one place. --- ## October 2026 ### What’s new 🌅 **Stop emailing contacts who no longer read your emails:** Sending to people who never open your emails hurts your sender reputation. The sunset policy stops marketing emails to contacts who haven’t opened or clicked for a time you choose, from 30 days to 2 years. It’s off by default, and you can turn it off for a single email or journey email step. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#turn-on-the-sunset-policy) 🛡️ **Email opens and clicks now count real readers:** Mail security programs, such as Microsoft Defender, open emails and click links before the recipient does. Email statistics now leave these out, so open and click rates reflect real readers. Older emails can still include them. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners) 🔘 **Add buttons to a push:** Let users act right from a mobile push. Add up to 3 buttons, such as **Track order**, that open a link or an action in your app, and see how many people tap each one. Your developers need to update the app to the latest Pushwoosh SDK first. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/#add-buttons-to-the-notification) 📬 **Confirm unsubscribes and ask why people leave:** Keep subscribers who click Unsubscribe by accident. The link opens a confirmation page first. Add a short survey and save the reasons in a tag, and design the subscription pages in your own style. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/unsubscribe-flow/) 📰 **Turn a news feed into automatic messages:** Pick a feed once, and every new article goes out as a push, an inbox message, or both, instead of being sent by hand. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/feed-campaigns/) 💰 **See which campaigns bring revenue:** Not sure which messages lead to purchases? The new Conversions tab in Project overview shows which one-time messages and journeys led to a conversion and how much revenue they brought, in each currency. Choose the events that count as conversions and how long after a message a conversion still counts. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#conversions-tab) 🎯 **Run several control groups at once:** Do your messages work as well in Germany as in Brazil? Run a separate control group for each market or audience and compare the lift your messages bring in each one, period by period. Keep a group permanent, refresh it on a schedule, or end it on a date you set, and limit it to users with a tag. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/#choose-a-mode) 🧲 **See which events a message drove:** Message and journey stats now show the events your app sent after users clicked, for example a purchase. Each event is credited to the last message the user clicked within 72 hours. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#custom-events-after-a-tap-on-this-push) 📉 **Know why emails bounce:** See the reason behind each bounce, which mailbox providers bounce your emails, and why each address on your suppression list was blocked, so you know what to fix. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#bounces-by-reason) 💬 **Reach customers with RCS messages through Twilio:** Make your texts stand out with images, carousels, and buttons right in the phone’s own messaging app, using your Twilio account. Phones without RCS get the text version as a regular SMS, and a journey can follow up based on the button a user taps. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/) 🌐 **Show web messages inside your page and collect sign-ups with forms:** Reach visitors without interrupting them. Show content as a block inside the page instead of a popup, and grow your email list with sign-up forms that save visitors’ answers to use in segments. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#start-a-new-campaign) 🏁 **Switch features on and off with feature flags (beta):** Want to try a feature on a few users before everyone gets it? Turn it on or off in your app or website for chosen users, with no new release, and see who saw it. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/feature-flags/) 📅 **Get statistics by email on a schedule:** Tired of exporting statistics by hand? Project statistics now arrive as a CSV every day, week, or month. Add up to 25 recipients, such as managers or an agency that doesn’t use the Control Panel. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/auto-reports/) ### Integrations 🔗 **WooCommerce:** Connect your WooCommerce store to start journeys from orders and abandoned carts, and add web push to the store. [Learn more](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/) 🔗 **Canva:** Bring designs from Canva into Pushwoosh content without exporting and re-uploading files. Edit a stored image in Canva and save it back as a new version. [Learn more](https://docs.pushwoosh.com/product/integrations/canva-integration/) 🔗 **Gmail Postmaster:** Connect your sending domain to see how Gmail rates your mail right in Project overview: the share of mail users report as spam, what Gmail rejects and why, and whether your domain meets Gmail’s sender requirements. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#connect-your-domain-to-gmail-postmaster) 🔗 **Google Analytics 4 audiences as segments:** Bring a GA4 audience into Pushwoosh as a segment and message it like any other. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/) 🔗 **Event Streaming pauses itself when your server stops responding:** If the server that receives your events accepts nothing for 24 hours, Pushwoosh pauses the stream and emails the account owner, so you notice and fix it. [Learn more](https://docs.pushwoosh.com/product/integrations/event-streaming/) ### Improvements #### Customer Journey 🎰 **Multi-Armed Bandit picks the winner by events:** Let the winning push be the one that leads to an event you choose, such as a purchase, not only to opens. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/multi-armed-bandit/) ⚡ **Show an in-app message right when the user acts:** An In-App step can now wait for an event you choose, such as opening the cart, and show the message in the same session. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/#choose-when-to-show-the-in-app) ↩️ **Catch messages the provider rejects:** Now a journey step can send users whose SMS, RCS, WhatsApp, LINE, or Messenger message the provider refused down a separate branch, for example to try another channel. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/#branch-when-the-provider-rejects-the-message) 🗂️ **Archive and delete journeys in bulk:** Archive inactive journeys and delete drafts, up to 100 at a time, instead of one by one. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journeys-list/) 📈 **Stats for messenger steps in journeys:** Journey statistics now cover Kakao, Telegram, Viber, and Facebook Messenger steps. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/) 📱 **Android Live Updates in the Live Activity step:** One Live Activity step now updates the iPhone lock screen card and the Android 16 Live Update notification together. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/#fill-in-the-android-notification) #### Channels 🎠 **Show several offers in one Viber message:** Send a carousel of 2 to 5 swipeable cards from a journey or through the Messaging API. In a journey, start from a carousel promo template. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/#send-a-carousel) ☑️ **Pick Marketing or Transactional for every one-time push:** A new one-time push now starts with no type selected. Choose Marketing or Transactional before you send it, so every message follows the right delivery rules. For example, an order update goes out as transactional. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) 📵 **Marketing consent for SMS and Viber:** Pushwoosh records whether each phone number agreed to marketing messages. Numbers that opt out, by replying STOP or with their mobile operator, stop getting marketing SMS and Viber. Opt-in mode, where only numbers that agreed get marketing, is available on request. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/) 🎟️ **Easier Wallet setup:** Start sending Wallet passes sooner. AI Assistant walks you through Apple Wallet setup with no Mac needed, and Google Wallet connects without a key file from your Google account. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/) 🔎 **Simpler page rules for web campaigns:** Choose where a web campaign shows by pasting the page address from your browser, and Pushwoosh handles capital letters and special characters for you. Needs an up-to-date Web SDK. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#show-on-pages) 🚦 **Show web campaigns by push permission:** Invite only the visitors who haven’t subscribed to push yet, or show a campaign only to your subscribers. Needs an up-to-date Web SDK. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#push-permission) #### Content 🖼️ **Stock photos and AI images in Media storage:** Find the right picture without leaving Pushwoosh. Pick a free stock photo or generate an image with AI right where you choose an image for a message. You can also have PNG files compressed on upload and reuse an earlier version of an image. [Learn more](https://docs.pushwoosh.com/product/content/media-store/#add-a-stock-photo-from-pexels) 🌍 **Get push content ready for a new market:** Adding a language your users don’t speak yet now takes one checkbox in the language picker, so the translation is ready before you launch in a new country. You can also find a language by its code, such as `ja`. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/#define-the-languages-you-want-to-use-in-your-push) 🛍️ **Product recommendations on your website:** Show recommended products from your catalog on your site with the Web SDK, not only in messages. [Learn more](https://docs.pushwoosh.com/product/content/product-catalog/) ❎ **Close button action in in-apps:** Add a button that simply closes the in-app message. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/built-in-editor-blocks-reference/#button) #### Audience and data ➕ **Segment by the sum of event values:** Find users by the total of a numeric event attribute, such as everything they spent this month. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/) 🧾 **See why email addresses were skipped in a contact import:** The import report now shows how many addresses were skipped for each reason, such as disposable addresses or addresses on your bounce list. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/#rows-skipped-during-import) ✏️ **Change a subscriber’s email or phone number right in the Control Panel:** When a subscriber gets a new address or number, change it on their existing channel in User Explorer instead of deleting the channel and adding a new one. They keep their tags and subscription status. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#change-an-email-address-or-phone-number) 🕵️ **Open a user in User Explorer without searching:** Check a new install or a test device in one click. User Explorer now lists your newest devices and test devices, so you don’t need to look up an ID. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#browse-recent-and-test-devices) 🔔 **Segment by push permission:** The Push Permission Status tag shows whether each user allows push, so you can target those who don’t with other channels. It fills in once users get an app or site version with an up-to-date Pushwoosh SDK. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/push-permission-status/) 📴 **Know when users turn push off or on:** The new `PW_PushAlertsChanged` event fires when a mobile app user switches notifications off or on in the device settings. Use it in segments and journeys, for example to reach users who just turned push off through another channel. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_pushalertschanged) 🧹 **Delete in bulk:** Delete tags, events, segments, presets, and templates several at a time. Items used by other content are skipped, and running campaigns that use a deleted segment stop after you confirm. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#delete-several-segments) #### Reports 🧭 **Channel reach on Project overview:** See how many users you can reach in at least one channel, in two or more, or in none, so you know how much of your audience a message can actually reach. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#channel-reach) ❗ **Know why a message wasn’t sent:** A message that failed now shows the reason in its stats, such as a deleted segment or an invalid send date. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed) 📊 **Project metrics in the projects list:** Compare audience, new contacts, sends, and opens across all your projects at a glance. [Learn more](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#projects-page) 👆 **See clicks in web campaigns:** See how visitors engage with any web campaign. Clicks on links and buttons now count, not only form submissions. Needs an up-to-date Web SDK. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/#what-counts-as-an-interaction) #### Account and security 🔐 **Personal data permission:** Choose which roles can open User Explorer, export audiences and recipients, and manage API tokens. New roles don’t get it by default. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) 🤖 **AI Assistant asks before it acts:** Stay in control of what AI Assistant does. Before it sends a message, or changes or deletes something, it shows the action and waits for your approval. You can approve several actions at once. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant-how-to-use/) ### Deprecations ⛔ **Project statistics is replaced by Project overview:** See the state of your project on one screen with no report to set up. Audience, retention, and push and email results are ready when you open it, and the revenue your messages bring appears once you choose your conversion events. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) ## September 2026 Facebook Messenger channel, Wallet passes and Live Activities in journeys, Holdout Split, journey versions, Project overview, self-serve data erasure, App Inbox channel, and improvements. ##### What’s new 💬 **Facebook Messenger joins Pushwoosh as a channel:** Reach customers in their Messenger chats through your Facebook Page. Set up a journey to reply when someone writes to your Page, send order updates, or run a one-time campaign alongside push, email, and SMS. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/) 🎟️ **Issue Wallet passes from a journey:** Issue an Apple or Google Wallet pass at the right step of a journey, then send the install link in a push or email. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) 📱 **Live Activities in journeys:** Keep customers posted right on their iPhone lock screen. A journey can show a live card with a delivery’s arrival time, a flight’s gate, or a match score, update it as things change, and remove it when it’s over. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) 🎯 **Measure a journey’s impact with Holdout Split:** Hold out a share of users at any point in a journey and compare their goal conversion with users who continued, so you know whether the journey actually moves the needle. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) 🕒 **Edit live journeys without disrupting users mid-flow:** Update a running journey, and users already inside finish the path they started while new users follow the updated version. A changelog shows what changed. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-history-and-versions/) 🏠 **Project overview page:** See how your project is doing at a glance, from audience growth to push and email results, without pulling a report together by hand. Export the numbers as a CSV when you need them elsewhere. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) 🗑️ **Erase user data without Support:** Remove personal data from Pushwoosh yourself, for many users at once from a file or for one user at a time. See how many records match before anything is erased. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/data-requests/) 📡 **Reachability check in journeys now covers several channels:** This journey step can now check several channels in the order you set, for example push, then email, then SMS. Each user is sent to the first one they can actually receive, so nobody is lost just because push is off. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) 📥 **Send messages straight to the in-app inbox:** App Inbox is now its own channel, with its own content, audience, and schedule, so a message can land in the inbox without a push. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/send-app-inbox-messages/) 🛒 **Build push content from a catalog or feed:** Create a push preset once and let Pushwoosh fill it from your Product Catalog or a news feed at send time, instead of writing each item’s content by hand. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/build-push-content-from-a-feed/) 📊 **See where a message lost its audience:** Message stats now show the whole path from audience to delivered to opened, explain why some messages failed, and split multichannel sends by channel, so you know exactly what to fix. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/) 💰 **Sell ad space in your emails and pushes:** Run advertisers’ campaigns in email ad slots and sponsored pushes from one place, and cap how many times each ad goes out across both. Available on request. [Learn more](https://docs.pushwoosh.com/product/content/email-content/email-ad-inventory/) ✅ **Version history and approvals for content:** Push presets and email templates keep your recent versions, so you can compare and restore earlier ones. Teams can also require a reviewer to approve content before it’s sent (available on request). [Learn more](https://docs.pushwoosh.com/product/content/approvals-and-content-versions/) 📖 **Tell a story in a push on iPhone:** A push now expands into a set of swipeable story slides. Send it as a one-time message or from a journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/ios-push-stories/) ##### Integrations 🔗 **Connect journeys to your other tools without building a webhook:** 25 ready-made journey points send data where your team already works. Update a CRM contact, track an event in analytics, add a row to a spreadsheet, post to team chat, or have AI write message text. Available on request. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/integrations/) 🔗 **SMSAPI.ro joins the SMS provider list:** If SMSAPI.ro is already your SMS provider, send through it from Pushwoosh. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) 🔗 **Flight Status integration:** Watch a passenger’s flight through AeroDataBox from inside a journey, and react the moment its gate, status, or arrival changes. [Learn more](https://docs.pushwoosh.com/product/integrations/flight-status-integration/) ##### Improvements ###### Customer Journey 📣 **Google Ads Customer Match audiences from a journey:** Add users to a Google Ads audience or remove them from it at any step of a journey, just as with Meta. For example, stop showing an ad to people who already bought. [Learn more](https://docs.pushwoosh.com/product/integrations/google-ads-integration/) 🖼️ **Attach media to WhatsApp journey messages:** Send an image, video, or document from a WhatsApp step when text alone isn’t enough. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/) 🔘 **Richer Viber journey messages:** Add an image and a button to a Viber step, so the message shows the offer and takes people straight to it. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/viber/) 🧰 **Journey branching and editing:** Branch users by how many values a list tag holds, check every Webhook call and its response, and see whether your canvas edits are saved. See [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#split-flow-by-tag), [Webhook](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#calls-log), and [saving edits](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#save-changes). 🧩 **More flexible Webhook step:** Add journey and device details to your requests with a placeholder, like `{{device:platform}}`, so the service you call gets the context it needs. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) 🚪 **Close entry to a running journey:** Stop new users from entering an audience-based journey from a date you choose, while people already inside finish it. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#when-entry-closes) ###### Email 🪄 **Brand book logo as an email header:** Add your Brand book logo to an email as a ready-made header, with no one-off styling each time. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#logo-header) 📩 **Send a test email to anyone before launch:** When you check an email before sending, you can now send the test copy to any address, not only saved test addresses. Handy when a colleague or client wants to see it first. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email) 📧 **Easier email domain fixes:** Open your domain’s settings and Pushwoosh tells you exactly which record to fix. If verification stalled, restart it in one click instead of redoing the whole setup. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#check-domain-status-and-fix-issues) 🚫 **Email suppression list:** See every address Pushwoosh stopped mailing after a complaint or hard bounce, and why, then export the list or bring an address back. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/) ⚠️ **Early warning on rising bounces:** Get a warning when bounces rise sharply, before email sending is paused, so you have time to react. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#bounce-spike-warning-before-email-sending-is-paused) ###### Content 📨 **Import and export translations for email and push:** Move language versions in or out as a file instead of retyping each locale by hand. See [email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/set-up-your-email-in-the-editor/) and [push presets](https://docs.pushwoosh.com/product/content/push-presets/import-export-translations/). 📲 **Fill a push from a link:** Pushwoosh can take the title, text, and image for a push preset from the page you link to, so you don’t retype them. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/fill-content-from-link/) 🧪 **Catch a broken product feed before you connect it:** Product Catalog checks your feed first, so problems show up right away, not after the sync. [Learn more](https://docs.pushwoosh.com/product/content/product-catalog/) 📐 **More control over in-app layout:** Preview messages at real phone widths, and make a message narrower with empty space on the sides instead of full width. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/) ✨ **AI intros for news in emails:** Let AI write a short intro for each news item in the News block, in the tone you choose, so you don’t write each one by hand. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#write-article-intros-with-ai) ###### Messaging channels 📍 **Smarter geozones:** Outline any area, like a mall or a venue, instead of a circle, and start a journey when someone enters, stays, or leaves. Add many zones at once from a file. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/) 👋 **Personalized WhatsApp welcome messages:** Greet each new WhatsApp subscriber with a welcome message tailored to them. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/) 🔄 **Wallet passes pick up tag changes automatically:** With tag sync on, issued passes refresh when tag values change, so a new seat or balance appears without reissuing the pass. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/sync-pass-fields-from-tags/) 📶 **Show a Wallet pass near a beacon:** Link a pass to up to 10 in-store beacons, so it pops up on a customer’s iPhone right where it matters, like at the store entrance. [Learn more](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/) ###### Audience and segments 💵 **Revenue tracking with no setup:** Send any event with `__amount` and `__currency` attributes, and Pushwoosh counts it as revenue automatically. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) 🆔 **User ID segments from the identifiers you have:** Build a User ID segment from HWIDs, emails, phone numbers, or push tokens, not only User IDs, so your list works as it is. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/) 📈 **RFM segments that stay up to date:** RFM segments refresh on their own, so you don’t rebuild them by hand every time scores change. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/) 🌍 **Control groups by country:** Limit a control group to the countries you choose instead of applying it worldwide. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) 🗂️ **One contact file for every channel:** Upload one CSV with emails, phone numbers, and WhatsApp and Viber contacts instead of a file per channel. Rows that can’t be imported are flagged so you can fix and retry. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) 🗄️ **More databases for External Segments:** Build segments from Microsoft SQL Server, Microsoft Fabric, or MySQL databases too, without copying data into Pushwoosh. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/external-segments/) 📋 **Know what happened to your CSV tag import:** After an import, you see how many rows matched a user, how many devices were updated, and why rows were skipped. A file with a broken row is now rejected at upload, before anything changes. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/#4-check-the-results) ###### Reports 🔥 **Email click map:** Email statistics show which links get the most clicks, so you can see what readers act on. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) 🏷️ **Find one-time sends by name:** Give a one-time email or Facebook Messenger message your own name, such as “Black Friday promo”, and that’s how it shows up in Message History. No more scanning subject lines to find the right send. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) ###### Account 🕐 **Your time zone, set at sign-up:** Pushwoosh takes your time zone from your browser when you sign up with the regular form, an invite, or Google, instead of defaulting to UTC. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/#default-timezone) ## August 2026 Brand book, Wallet Pass Campaigns, named control groups, dynamic-value journey branching, native in-app video, live feeds in email and SMS, Web popup blocks, Smart check, SMS MMS, and improvements. ##### What’s new 🪄 **Brand book, one place for your brand styling:** Tired of styling every email and in-app message from scratch? Save your logo, colors, fonts, block defaults, and brand voice once, then apply them to any email or in-app template in one click. Pushwoosh can even build a starting kit for you, by scanning your website, pulling palette and voice from a file, or suggesting styles from your sent email history. [Learn more](https://docs.pushwoosh.com/product/content/brand-book/) 🎟️ **Manage passes at scale with Wallet Pass Campaigns:** Design one Apple or Google Wallet pass, issue it to any number of users, then push an update to every pass at once instead of editing them one by one. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/) 🎯 **Global control group now covers your whole audience instantly:** Turn it on, and Pushwoosh picks who skips your marketing messages from your entire audience right away, not just users who sign up going forward. Set the percentage you want, and coverage is immediate, no gradual catch-up. Want a different random group at the same percentage? Click **Reshuffle control group** anytime. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#how-control-group-membership-works) 🔀 **Branch journeys by comparing two user values:** A Condition split can now compare one user’s tag or event attribute against another attribute on the same user, instead of only a fixed value. For example, you can check whether a user’s cart total already covers their own free-shipping threshold, then send one message to users who qualify and another to users who still need to add more. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#compare-branch-values) 🎬 **Add video to native in-app messages, with dynamic text everywhere:** A new Video display type plays a full-screen video, good for showing off a new feature or a promo (iOS only for now). Text fields across native templates can now pull in a tag or User ID, so the same message can greet each user by name or show their own order or balance. See [Video](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#video) and [dynamic content](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-dynamic-content). 📰 **Pull live articles into email and SMS:** A News block in email and a News Feeds preset in SMS both pull the newest articles from a registered feed the moment each message sends, so content never goes stale. SMS can also rank articles by what’s trending. See [News block in email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#pull-articles-from-a-live-feed) and [News Feeds in SMS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#news-feeds). 🛍️ **Auto-fill the Products block with recommended products:** A Products block can now pull from your catalog for each recipient at send time: top sellers, items back in stock, things often bought together, and more. Every email shows a relevant, current lineup without you building or updating it by hand. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products) 🎨 **More Web popup blocks to cover more use cases:** Add a countdown, an icon, a topic picker, or a rating block to any popup, or start from a blank layout with a subscription form already in place. [Learn more](https://docs.pushwoosh.com/product/content/web-popup-content/web-content/#start-from-a-template) 📱 **Catch content issues in the in-app editor before you save:** **Smart check** scans your template for broken links, missing alt text, failing contrast, and other issues, so you catch them before your users do. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#check) 📷 **Turn an SMS into an MMS with image attachments:** Attach up to 3 images to an SMS preset, useful for showing off a product photo, a coupon, or a flyer instead of relying on text and a link alone. Delivery as MMS needs a provider with an MMS endpoint. Other providers send the plain-text message instead. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#add-mms-attachments) ##### Improvements 📲 **One-time pushes can now target just a user’s last active device:** This capability, already available in Customer Journey, now works for one-time pushes too. A user with several devices gets a single message on the one they opened most recently, instead of a copy on every device. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#send-to-last-active-device) 🚦 **Frequency caps that reset at local midnight:** When you limit how often users get marketing messages, pick **Calendar day** as the counting mode. The cap resets at midnight in a timezone you choose, instead of on a rolling 24-hour window from each send. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#configure-global-frequency-capping) 🚪 **Exit element removed from the journey editor:** A branch ends on its own when it has no more steps. You won’t see **Exit** in the element palette on new journeys. Journeys that already have it placed keep working as before. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) 📅 **Filter journeys by relative dates:** A Condition split on a Date event attribute can now filter by how many days ago or ahead a date falls, not just an exact calendar date. For example, message users 3 days before their subscription expires, or 7 days after they registered, each on their own personal schedule instead of one fixed date for everyone. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#split-flow-by-event) 🌓 **Dark mode comes to Web popups and in-app templates:** Both now support a dark theme alongside your light design. [Web popups](https://docs.pushwoosh.com/product/content/web-popup-content/popup-settings/#dark-mode) · [In-app templates](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-template-wide-styling) 📅 **Bound a geo campaign to a date range:** Set a start and end date on a geozone’s schedule, and the campaign runs only in that window, no need to remember to turn it off. Handy for holiday promos and other limited-time pushes tied to a location. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#schedule-when-the-campaign-can-fire) 📊 **See clicks and revenue per Products block in email statistics:** When an email has a Products block, its statistics now break out clicks and revenue for that block separately, instead of folding everything into one total for the whole message. With multiple Products blocks in one email, you can see which one actually drives sales. Revenue tracking requires [conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) set up on your account. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) 📥 **See a user’s Message Inbox without opening their app:** A new **Inbox** tab in **User Explorer** shows which messages landed in that user’s Message Inbox and whether they were opened. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#inbox-tab) 🎯 **Segment by email category, using names you recognize:** When the [preference center](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) is enabled, segments built on `PW_EmailSubscribed` and `PW_EmailUnsubscribed` let you pick the email category by its real name (Newsletter, Promotional, and so on) instead of a raw value, so you can target users by exactly what they opted in or out of. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/#categories-and-subscriptions) 💬 **WhatsApp: trace ad-driven chats, and see your reply window:** When a customer messages you from a Click-to-WhatsApp ad, the ad click and date now save to their profile so you can trace the conversation back to it. Separately, a WhatsApp conversation shows a countdown for the 24-hour window you have to reply in free text before you need an approved template. See [reply window](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/#send-free-form-messages-and-view-your-whatsapp-conversations) and [ad-click tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags). 📣 **See what’s new without leaving the Control Panel:** Click the megaphone icon in the top bar to view recent Pushwoosh features and improvements, each with an image, date, and description. [Learn more](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#product-updates) ##### Integrations 🔗 **Inbound webhooks now save to tags and auto-create users:** Map incoming fields to profile tags in addition to event attributes. Useful when you’re moving a customer database from a CRM or another tool and need fields like plan tier or city on the profile for segmentation. Turn on auto-create to register users Pushwoosh doesn’t recognize yet. [Learn more](https://docs.pushwoosh.com/product/integrations/inbound-webhook/#map-incoming-data) 🔗 **See what shoppers do before they buy or leave:** Pushwoosh can now track what happens on your Shopify storefront itself, like product views, add-to-cart clicks, and checkout steps, not just finished orders and abandoned carts. Use it to reach people who browsed but never added anything to cart, or who started checkout and stopped partway through. [Learn more](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/) ## July 2026 Conversion events, redesigned email and in-app editors, native in-app messages, Viber channel, Multi-Armed Bandit, Product Catalog, email audit, Apple Mail open tracking, and improvements. ##### What’s new 💰 **Conversion events:** Want to see exactly how much revenue your campaigns actually drive? Send a dedicated `PW_Conversion` event directly, or map it from an existing event using the new Conversion Events screen. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) 🎨 **Redesigned email and in-app content editors:** Build faster and smarter with a completely reworked editing experience. Personalize content for different audiences, draft and translate with AI, catch issues before you hit send, and assemble campaigns with new ready-made blocks, and that’s just the start. See [email editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/) and [in-app editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/). 📱 **Native (SDK-rendered) in-app messages:** In-app messages now render natively via the iOS and Android SDKs, so they open faster and feel like a native part of your app. Choose a display type and build templates from a dedicated Control Panel editor. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/) 💬 **Viber joins Pushwoosh as a channel:** Looking to reach customers on Viber? Configure the channel, then send Viber messages via Customer Journey or the API. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/viber-configuration/) 🎰 **Multi-Armed Bandit:** Running an A/B test on push copy means some users always get the losing variant until the test ends. The new Multi-Armed Bandit element in Customer Journey fixes this by routing traffic to the best-opening variant automatically, as opens come in, rather than waiting for a fixed test to reach significance. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/multi-armed-bandit/) 🛍️ **Product Catalog:** Sync your product feed into Pushwoosh once, then show live product data in your emails and in-app messages. Use the Catalog rule source to recommend products people might like, or the Cart source to bring back items they left behind. [Learn more](https://docs.pushwoosh.com/product/content/product-catalog/) 📩 **Audit your email before sending:** Never hit send only to spot a broken link or a missing unsubscribe footer too late. A new pre-send step now runs a Content check and a Compliance check on one-time emails, catching issues like these before they reach your subscribers. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#audit-your-email) 📊 **Real opens vs. Apple Mail auto-loads:** Get a clearer, more accurate read on email engagement. Email statistics now separate genuine opens from opens that Apple Mail Privacy Protection pre-loads automatically. A new `opened_by` attribute (`user` / `ampp`) lets you segment by either. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) 📈 **Analyze Global Control Group impact:** See how much your messaging actually drives. A new impact report compares your Global Control Group against everyone else, showing the uplift and conversions your campaigns generate for the events you choose to track. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#analyze-control-group-impact) ##### Improvements 🎨 **Web popup editor and targeting improvements:** The popup editor now gives you more room to design, and campaigns get more room to target precisely where they show. See [Web popup content](https://docs.pushwoosh.com/product/content/web-popup-content/) and [campaign targeting](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/#show-on-pages). 💬 **Send one-time WhatsApp messages:** Pick an approved template, choose your audience, and send now or on a schedule, no journey required. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/) 📋 **Use a subscription form as your Web popup content:** Turn any popup into a lead-capture moment. A Subscription form block can now be added to any popup, linking an existing form or creating one without leaving the editor. [Learn more](https://docs.pushwoosh.com/product/content/web-popup-content/blocks/#subscription-form) 🔗 **Bring webhook response data into a journey:** When a webhook step calls an external service, you can now use the data it sends back. Pull a value from the response and use it right away to personalize later steps in the same journey run, or add an Update user profile step to save it as a Tag for future use. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/using-webhook-response-data-in-journeys/) 📲 **Animation timing for modal Rich Media:** Fine-tune how a modal Rich Media feels on screen. Set how long it takes to appear and dismiss, from the Control Panel or the iOS/Android SDK. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) 💬 **New send events for SMS and WhatsApp:** Want to segment or trigger journeys based on whether a message actually went out? New `PW_SMSSent` and `PW_WhatsAppSent` events record when an SMS or WhatsApp message is sent, the same way `PW_EmailSent` already does for email. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/) ✏️ **Rename subscription form field labels:** Field labels on subscription forms can now be changed to match your brand voice or audience, right from the editor, no code required. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/create-subscription-forms/) ## June 2026 Apple and Google Wallet passes, one-time and self-serve SMS, Media storage, frequency capping statistics, Android Live Updates, Singular integration, subscription tracking, and improvements. ##### What’s new 🎟️ **Apple and Google Wallet support:** Create loyalty cards, coupons, tickets, and boarding passes in the Control Panel or via API, then share them as a link or QR code. Updates sync to every saved device automatically. See [Wallet passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/). 💬 **One-time SMS campaigns:** Send SMS without setting up a journey. Choose message type, reuse multilingual presets, and edit a scheduled send before it goes out. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/sms/) 📱 **Self-serve SMS provider setup:** Set up SMS on your own. Choose a supported provider in the Control Panel and connect it in minutes. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) 🖼️ **Media storage:** Upload images once to your project library and reuse them across your messaging. Pick them directly in the push editor or copy a stable URL to use anywhere. Create aspect-ratio variants when the same image needs different proportions. [Learn more](https://docs.pushwoosh.com/product/content/media-store/) 🚦 **Frequency capping statistics:** See exactly how sending limits affect delivery, with suppressions and affected users now visible in push, email, and customer journey reports, plus a dedicated block on the project dashboard. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#view-frequency-capping-statistics) 🚚 **Android Live Updates:** Show progress for deliveries, rides, and other ongoing activities in a single ongoing notification that refreshes in place as the activity moves forward. Available on Android 16+ via the Pushwoosh Android SDK and Messaging API v2. [Learn more](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/live-updates/) ##### Integrations 🔗 **Singular integration:** Bring Singular attribution data into Pushwoosh for sharper segments and personalization. Connect the postback and turn incoming attribution into tags and events you can target. [Learn more](https://docs.pushwoosh.com/product/integrations/singular-integration/) 🔗 **App Store and Google Play subscription tracking:** React to every stage of the subscription lifecycle on iOS and Android. Connect App Store Server Notifications or Google Play Real-Time Developer Notifications and trigger journeys or segments from purchases, renewals, cancellations, billing issues, and refunds. See [App Store subscription tracking](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/) and [Google Play subscription tracking](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/). ##### Improvements 📲 **Send journey pushes to the last active device:** Reach users where they actually are. A new toggle in the Customer Journey Push element limits delivery to the most recently active device. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#send-to-last-active-device) 🕐 **Set a timezone for instant in-app campaigns:** Pick the timezone on the Display settings step so start and end times for instant in-app campaigns always run in the right local context. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/#set-the-timezone-for-campaign-schedule) 👁️ **Unique Impressions and Unique Interactions for in-apps:** Measure real reach, not just event volume, with two new metrics counting distinct users who saw and engaged with an in-app message, alongside existing totals. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) 🎬 **In-app animation options:** Control how in-app messages enter and exit the screen, with None, Fade, or Default available in the rich media editor. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/configure-in-app-template-settings/#configure-how-the-in-app-looks-on-device) 💾 **Autosave in the rich media editor:** Never lose in-progress creative work. The editor keeps a draft in your browser, and reopening a template offers to restore unsaved changes, kept for up to 7 days. [Learn more](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/#save-the-template) ✍️ **Improved SMS builder:** The preset editor now shows character count, encoding, and message parts live as you type, so you can see what drives cost. Inline link shortening helps keep messages tight. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/) 🔎 **Filter received messages by type in User Explorer:** Investigate a user faster by filtering their received-message list by type when reviewing activity. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#received-messages-tab) ✅ **Validate Liquid syntax in push presets:** Catch broken personalization before it ships. Check Liquid markup in the title, subtitle, and message for syntax errors before you save. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/#validate-liquid-syntax) 🌍 **Work in your language:** The Pushwoosh Control Panel is now available in 14 languages. Switch the interface language from **My Account**. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/#interface-language) 🗂️ **Message History consolidated in Campaigns:** Find all your sent and scheduled messages in one place, under Campaigns → One-time messages, now with a refreshed list view, new filters, and clearer statuses. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) 📊 **Refreshed message statistics page:** A cleaner, more functional layout for reviewing message and campaign performance. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#view-detailed-statistics) ## May 2026 Web popups, Messaging MCP server (beta), geo & User ID segments, Finance metrics in Dashboards, BigQuery streaming, inbound webhook, Meta Ads sync, and improvements. ##### What’s new 🎯 **Convert visitors with the new Web popups:** Capture leads, promote offers, or re-engage returning visitors. Build popup content using AI Builder and reach the right audience with campaign targeting. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) 🤖 **Send messages from your AI client with the Messaging MCP server (beta):** Your AI agents can now handle multi-channel messaging. Connect any MCP-compatible client to send push, email, SMS, WhatsApp, Telegram, LINE, or Kakao from a natural-language prompt. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-ai/messaging-mcp-server/) 🌍 **Build segments by geolocation and User ID:** Get more control over who receives your messages. Target users near a specific location within a chosen radius, or build a segment from a list of User IDs. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo/) 📈 **Track revenue with the new Finance metrics in Dashboards:** After you connect Stripe, add Gross Revenue and New Subscriptions widgets to any dashboard, or use the auto-created Finance Overview dashboard to monitor monetization in one place and quickly spot revenue trends by campaign. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#finance-metrics) ##### Integrations 🔗 **Stream message events to Google BigQuery:** Send push, email, and SMS lifecycle events straight to your BigQuery dataset. Build custom reports, track campaign performance, and connect Pushwoosh data to your existing analytics workflows. [Learn more](https://docs.pushwoosh.com/product/integrations/google-bigquery-integration/) 🔗 **Trigger journeys from third-party services with the Inbound webhook:** Turn events from any external system into personalized journeys. Connect your CRM, e-commerce platform, or analytics tool, and let Pushwoosh react to real customer behavior in real time. [Learn more](https://docs.pushwoosh.com/product/integrations/inbound-webhook/) 🔗 **Sync Meta Ads audiences from Customer Journey:** Add or remove users from Meta Ads audiences directly inside your journey flows, so your ad targeting stays aligned with real user behavior. [Learn more](https://docs.pushwoosh.com/product/integrations/meta-ads-integration/) ##### Improvements 👤 **User Explorer subscriber view got a major update:** The new user-centric layout makes it faster to find any subscriber, understand their channels, and review their full activity in one place. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/) 📍 **Redesigned Geo Campaigns app:** The geo campaigns interface has been redesigned for easier management. Group geozones into clusters, set shared cooldowns, and schedule delivery by day and time window from a single map view. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/) ⏱️ **Set TTL on push notifications:** Make sure time-sensitive messages only reach users while still relevant. Add a Time-To-Live to one-time push sends and Customer Journey Push steps so users never receive an outdated message. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#set-time-to-live-for-push-delivery) 🔍 **New string tag operators in Segment Builder:** Filter users by partial text in any string tag using three new operators: **starts with**, **ends with**, and **contains**. This allows you to build more precise audiences. For example, you can target users whose plan contains *enterprise*, whose promo code starts with *VIP*, or whose email ends with *@company.com*. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#string-tag-operators) 📘 **Expanded Date tag operators in Segment Builder:** The Date operator list now covers relative future ranges like **between N and M days ahead**, making it easier to target users before an upcoming event, trial expiry, or renewal date. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/#date-tag-operators) 📩 **New tags for email engagement segmentation:** `Last Email Open Message Code` and `Last Email Click Message Code` track which specific email a user last opened or clicked. Building engagement-based segments just got faster—no more filtering through event history. Use them to re-engage users who opened a specific campaign, suppress recent clickers from a follow-up, or reward users who engaged with a promo. Each tag stores only the most recent value. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) 📊 **Richer Message History CSV export:** CSV exports now include both unique and total counts for opens, clicks, and deliveries, plus the **Total unsubscribed** column. If you process this export automatically, review your scripts: **Total opened** and **Total clicked** now include repeated events, while unique values are in **Total opened unique** and **Total clicked unique**. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#export-message-history) 🎨 **Customer Journey canvas redesign:** Flows are easier to read and navigate at any zoom level, large journeys stay smooth on the canvas, and you can rename and manage a journey directly from its statistics page. 📋 **Clone in-app messages:** Duplicate any in-app message from the list and tweak it without rebuilding from scratch. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/#take-action-on-a-campaign) 📲 **WhatsApp received event now carries location:** `PW_WhatsAppReceived` now includes `latitude` and `longitude` attributes, so you can use WhatsApp location messages as journey triggers. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_whatsappreceived) ## April 2026 ManyMoney AI MCP server, Telegram in Customer Journey, passkeys, subscription forms, redesigned Billing, Cloud Pages, AI Email Builder, PW_ApplicationExit, fallback flow splits, and improvements. ##### What’s new 🤖 **Run Pushwoosh from your favorite AI client:** Connect ManyMoney AI to Claude Desktop, Cursor, Windsurf, or any other MCP-compatible client and drive your project from there. Build segments, presets, and customer journeys, diagnose delivery issues, and analyze campaign performance, all from natural-language prompts in the AI tool you already use. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-ai/manymoney-mcp-server/) **📲 Send Telegram messages in Customer Journey:** Connect a Telegram bot and send messages as part of automated flows to handle onboarding sequences, reminders, and follow-ups. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/telegram/send-telegram-via-customer-journey/) 🔑 **Use passkeys for faster, secure sign-in to Pushwoosh:** Set up a passkey and use your device to confirm access instead of typing a password or waiting for a code. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/passkeys/) 📋 **Collect email subscribers with subscription forms:** Build modal or inline subscription forms directly in Pushwoosh, embed them on your website, and have new subscribers synced to your audience automatically. Enable double opt-in to verify each address before it’s added to your list. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/) 💳 **Track plan costs and usage in the redesigned Billing page:** The updated billing page makes it easy to see what’s in your plan and what you’re paying for. Plan costs, usage stats, invoices, payment details, and AI wallet balance are all consolidated in one place. [See your billing](https://app.pushwoosh.com/accounts/billing) 🌐 **Build campaign landing pages directly in Pushwoosh:** Cloud Pages let you create promo pages, lead capture forms, and quizzes in a visual editor and publish them instantly. Use form submissions as triggers in Customer Journey. Available on request via Pushwoosh Support. [Learn more](https://docs.pushwoosh.com/product/content/cloud-pages/) ✨ **Create email content with AI Email Builder (beta):** Type what you need or choose a prompt, and get ready-to-edit email content straight in the editor. 🏃 **Track confirmed session exits with PW_ApplicationExit:** A default event that fires when a user leaves the app and does not return within a set timeout. Use it to trigger a win-back push or email after a dropped session. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_applicationexit) ##### Improvements 🔀 **Set up fallback channels using delivery-based flow splits:** Customer Journey now lets you split push notification flows based on delivery status. If a push is not delivered to the user’s device, you can automatically route them to a fallback channel such as email, SMS, or in-app messaging so they still receive your message. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#split-flow-by-message-delivery) 🎛️ **Control who enters Audience-based journeys:** Choose whether to include existing users, new users, or both when the journey starts. Useful when you want to onboard only new signups, run a one-time campaign for your existing base, or keep both groups in sync in an always-on flow. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#define-who-enters-the-journey) 🚫 **Exclude specific users from frequency capping:** Some accounts should always get your messages regardless of sending limits. Add them by User ID or email to bypass capping entirely. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#exclude-users-from-global-frequency-capping) 📤 **Select fields before exporting segment users to CSV:** Choose which data fields and tags to include instead of downloading the full user profile. Handy when you need a clean list for a specific campaign upload or want to share segment data without exporting unrelated attributes. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/#export-segment-users) 📬 **Manage email subscription categories more flexibly:** Set up categories with options to automatically subscribe new or existing users on save, and import subscription categories in bulk via CSV. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) 📨 **Use different subject lines and senders across email steps in Customer Journey:** Each Email element can now have its own subject line and sender, set manually or taken from a preset. Useful when a single journey sends emails from different addresses or with different subjects at each step. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-the-subject-line) ## March 2026 Marketing and transactional message types, resend to non-openers, custom tracking domain, AppsFlyer ESP 2.0, email stats updates, in-app list refresh, and fixes. ##### What’s new ⚙️ **Marketing vs. transactional messages across channels:** Select the message type for push, email, in-app, SMS, and WhatsApp messages. Use **Marketing** for campaigns and promotions that respect opt-outs, frequency limits, and silence periods, and exclude the global control group. Use **Transactional** for essential messages such as order updates or alerts that must reach all users while bypassing those rules. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) 📨 **Resend emails to non-openers:** Give your emails a second chance. Resend campaigns to non-openers using a toggle in the **Review** step when setting up a one-time email. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#resend-to-non-openers) 🌐 **Custom tracking domain for email links:** You can now set up a custom tracking domain for email links, so tracking URLs match your brand domain. [Learn more](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#set-up-a-custom-tracking-domain) ##### Integrations 🔗 **Introducing AppsFlyer ESP 2.0 Integration:** Connect Pushwoosh to AppsFlyer to track email clicks and see performance alongside your other channels. [Learn more](https://docs.pushwoosh.com/product/integrations/appsflyer-esp2-email-integration/) ##### Improvements 📊 **Opened total in email statistics:** Opened total is now part of email statistics. Compare it with Opened unique to get a fuller picture of how your messages perform. ✏️ **Edit subject lines in one-time email campaigns:** Need to tweak the subject line on a scheduled email? Update it in **Review**, without reopening the builder. 📲 **Refreshed in-app messages list:** Filters and key metrics are now available directly in the list view for faster performance analysis. ##### Fixes 🛠️ Version selection in the Condition Split block now works reliably, with tags applied and saved correctly. 🛠️ Rich media in Customer Journey loads faster and more smoothly. 🛠️ Message statuses stay consistent across the UI. ## February 2026 Email Preference Center, Global Control Group, audience-based entry updates, scheduled one-time edits, WhatsApp frequency capping, Stripe integration, voucher and platform UI updates, deprecations, and fixes. ##### What’s new **⚙️ Give subscribers control over what they receive:** With the new **Email Preference Center**, users can select exactly which types of emails they want to receive, including newsletters, promotions, product updates, and more. Instead of opting out completely, they can fine-tune their preferences and stay connected to the content that matters most to them. This helps reduce full opt-outs and keeps your audience more engaged. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/) **👥 Measure campaign impact with Global Control Group:** The new Global Control Group lets you exclude a fixed percentage of your user base from all marketing communications (push, email, and in-app) while still delivering transactional messages to them. Compare their behavior against users who received your campaigns and get a clear, data-backed picture of your messaging effectiveness. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) **🔄 Let users who qualify later automatically enter a journey:** Enable a new setting to automatically add users to a journey when they begin matching your audience segment, even after the journey has launched. Whenever a user becomes eligible, they’ll enter the journey automatically, with no manual updates required. Ideal for ongoing campaigns like onboarding, loyalty, or win-back flows. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#add-users-when-they-start-matching-the-segment-later) **✏️ Edit or delete scheduled one-time messages:** You can now edit scheduled one-time push notifications and emails after creation. Update the content or refine your audience without starting from scratch. If plans change entirely, you can delete the message before it goes out. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push/#cancel-or-edit-a-scheduled-message) **📊 Limit how often users receive WhatsApp messages:** Global frequency capping is now available for WhatsApp. Set a message volume limit across all WhatsApp campaigns to avoid over-messaging and keep your audience engaged. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) ##### Improvements **🗑️ Clean up your voucher library:** You can now delete individual vouchers or entire voucher pools. Useful when a promotion has ended, vouchers have expired, or you need to remove a batch created by mistake. Keep your library organized and easy to navigate as it grows. **📱 Configure platforms page is now easier to navigate:** Finding and managing your channels is now easier. Hide unconfigured platforms to keep the view focused on what you actually use. **🗓️ Configure Audience-based Entry scheduling with less effort:** We’ve updated the interface to make it easier to configure your scheduling settings. ##### Integrations **💳 Connect payments to your campaigns with Stripe:** Stripe is now available as a payment integration in Pushwoosh. Track payments and subscriptions directly in your campaigns, segment users by payment events, analyze revenue by journey and product, and get AI-powered revenue insights with ManyMoney. [Learn more](https://docs.pushwoosh.com/product/integrations/stripe-integration/) ##### Deprecations **⛔ Autopushes fully retired:** Switch to Customer Journey with Audience-based Entry and Scheduled Launch for recurring, segment-based automation. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/autopushes/) **⛔ CSV push is gone:** Sending immediate pushes to CSV-imported contact lists is no longer supported. Use One-Time Push and select Import Segment at the Audience step to upload a CSV and target those users in a single flow. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push/#send-to-segment) ##### Fixes **🛠️** Fixed a display issue with the Notification Center on the AI Assistant page. ## January 2026 Notification Center, save to Inbox, cancel events, KakaoTalk, segment counts in journeys, dashboard updates, and upcoming deprecations. ##### What’s new **🔔 Stay on top of your projects with the new Notification Center:** Get real-time alerts in a central hub, with direct links to your projects and apps. Instantly see what needs attention and jump straight to the relevant section in Pushwoosh. [Learn more](https://docs.pushwoosh.com/product/first-steps/pushwoosh-interface-overview/#notification-center-top-right-corner) **📬 Save one-time messages to Inbox:** You can now save one-time push notifications to the app’s Inbox, so users can revisit important messages anytime, even if they missed the original alert. You can also control how long messages stay and add icons to highlight key content. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push/#save-message-to-inbox) **🚫 Automatically remove users from journeys based on their actions:** You can now define specific user actions, such as reaching a milestone, unsubscribing, or updating preferences. That will immediately remove them from a journey. This helps keep your conversion metrics clean and focused. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#cancel-events-remove-users-from-a-journey-without-counting-a-conversion) **💬 Reach your audience with KakaoTalk messaging:** KakaoTalk is now available as a messaging channel. Send personalized, template-based messages via Customer Journeys or API to connect with users in South Korea. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/kakao/) ##### Improvements **📊 View segment sizes in Customer Journey entries:** Audience-based entry steps now display real-time segment counts, so you can see how many users will enter a journey before launching or making changes, making it easier to plan and optimize. **💾 Autosave for chart widgets:** Drafts in the Analytics Dashboard are now saved automatically. If you leave with unsaved changes, your progress is restored when you return, so you can continue right where you left off. **📈 Improved dashboard UX:** Enhanced chart rendering, clearer empty states, a redesigned calendar, and overall UI updates for a smoother, more reliable experience. ##### Fixes 🛠️ Fixed stats display issues on the Customer Journey canvas. 🛠️ Enhanced rich media preview for better accuracy and display. ##### Upcoming feature deprecations We are retiring two classic Pushwoosh features. Please migrate to the options below before the dates listed. **⛔ Autopushes (retiring February 7, 2026):** Autopushes allowed you to send push notifications automatically based on segment rules. Please use **Customer Journey** with **Audience-based Entry** and **Scheduled Launch** instead. This transition ensures you maintain the same recurring behavior while gaining more granular control over your automated workflows. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/autopushes) **⛔ Push to a contact list (retiring February 22, 2026):** This feature was used to send immediate pushes to lists imported via CSV. Instead, use the **Import Segment** option during the **Audience step** of a One-Time Push. This allows you to achieve the same results more efficiently within a single, streamlined message flow. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push#send-to-segment) ## December 2025 WhatsApp catalogs, MAU/DAU change, AI Wallet, Project Overview, segment cleanup, vouchers, CSV import, and more. ##### What’s new **🛍️ Send multi-product catalog messages in WhatsApp:** Want to showcase specific products in chat? Now you can. Select up to 30 items per section and group them into 10 sections to highlight curated collections. It’s a powerful way to guide users to the right products and increase conversions. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp/) **👥 We’ve changed how active users are calculated:** Monthly Active Users (MAU) and Daily Active Users (DAU) are now based on unique users instead of devices. For continuity, device-based metrics are still available under new names: MAD (Monthly Active Devices) and DAD (Daily Active Devices). [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#daily-active-users-dau) **💰 Keep ManyMoney AI running smoothly with the AI Wallet:** A new AI Wallet section on the Billing page lets you track your balance, add funds, or enable auto-refill to keep your AI-powered features running without interruption. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/billing-page/#ai-wallet) ##### Improvements **📊 Updated Project Overview:** The Project Overview section now features a cleaner design and new user charts, giving you a clearer snapshot of key metrics and overall performance. **🗂️ Smarter segment cleanup:** When deleting a segment, you now see which campaigns it’s used in, including journeys, one-time messages, and instant in-apps. This helps you avoid breaking active workflows. **🎟️ Download vouchers and track usage stats:** You can export voucher lists with key details like status, assigned user, and usage time. The Voucher Pools overview also now shows real-time usage and availability stats. **📥 Import segments directly into one-time messages:** Upload CSV files with user data when setting up one-time pushes or emails. This makes it easy to target specific audiences without building segments manually. **⬆️ Streamlined CSV import in Segment Builder:** The import UI has been refreshed for a smoother, more consistent experience when uploading segments. ##### Fixes 🛠️ User export from Audience in Customer Journey now shows clearer, readable date ranges. 🛠️ Improved stability and reliability of split conditions in Customer Journey. ## November 2025 ManyMoney AI, promo codes and catalogs in WhatsApp, communication statistics, auth history, and more. ##### What’s new **🤖 Meet ManyMoney AI, your AI teammate for revenue growth:** ManyMoney AI is your new AI-powered marketer that builds and optimizes campaigns end-to-end. It creates message text and content, segments audiences, runs tests, localizes content, and continuously improves performance. Based on your goals, it can do much more. Simply define the outcome, and ManyMoney AI delivers the results. [Learn more](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant) **💬 Deliver promo codes in WhatsApp:** Want to send users a discount the moment they need it? You can now add simple placeholders to WhatsApp message templates that automatically insert the right promo code, whether it’s a unique one for each user or a single fixed code for everyone. This makes it easy to send rewards, incentives, and seasonal offers directly in chat, helping you boost engagement and conversions with perfectly timed messages. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-promo-codes-via-whatsapp/) **🛍️ Showcase your products in WhatsApp with catalog messages:** Help customers find the right products faster by sending full product catalogs directly in WhatsApp using Meta’s Catalog message format, all without leaving the chat. With Pushwoosh Customer Journey, you can trigger these messages at the perfect moment, making it easier to highlight your top items, guide users toward purchase, and boost conversions in a channel they already trust. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-catalogs-via-whatsapp/) **📊 Get clearer performance insights with Communication statistics in Customer Journey:** You can now see detailed performance stats for every communication channel in your Customer Journey, giving you a clear picture of how each message contributes to your flow. This makes it easier to track engagement, spot where users drop off, and fine-tune your journeys to deliver more effective and better-timed interactions. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#communications) **🔐 Review login activity easily with the new Auth history tab:** You now have a dedicated **Auth history** tab on the **Users & Roles** page, giving account owners and authorized users a clear view of all authentication activity. It’s an easy way to monitor logins, keep your workspace secure, and stay fully informed about how and when your account is accessed. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#view-authentication-history) ##### Improvements **🔎 Find the right journeys faster with improved filters:** The Journeys list now has a cleaner, more intuitive filter design that helps you quickly narrow results, including filtering journeys by when they were launched. **🔧 Test webhook for easier setup and verification:** Use the new **Test webhook** option to quickly confirm that your webhook is configured correctly and that your request is sent and received as expected. **💬 Manage your messaging channels more easily with the new Messenger section:** Messenger chats and message presets now live in their own dedicated sections, giving you a clearer and more organized workspace. **🔁 Manage multi-session settings in one place for smoother setup:** When you enable multiple active sessions in Trigger-based entry, a new **Multiple sessions settings** button appears on the canvas. It opens a single drawer that brings together all related attributes from Entry, Wait for Trigger, and Conversion Goals, making it easy to review and adjust your setup so everything stays aligned and working as intended. ##### Fixes 🛠️ Navigating back from an opened journey now preserves your filters in the Customer Journey list, ensuring a smooth and consistent browsing experience. 🛠️ The on-click action in preset details now accurately reflects the mobile behavior when a web link is configured. ## October 2025 Segment-in-segment support, event-based splits, AND NOT operator, in-app message customization, new email templates, and more. ##### What’s new 🧩 **Build smarter segments with segment-in-segment support:** You can now include users from existing segments when creating a new one — no need to rebuild complex filters from scratch! For example, combine your “Active Users” segment with a “Premium” tag to instantly target recently active premium users. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments) 🔀 **Make journeys more effective with event-based splits:** You can now branch your journey based on details from past user actions. Send tailored messages based on what users did, such as showing different content to people who viewed “Shoes” versus “Accessories.” [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#split-flow-by-event) 🎯 **Refine your targeting with the new AND NOT operator:** Need to exclude a specific audience when building a segment? The AND NOT operator lets you include users from one group while excluding those who meet another group’s conditions. For example, target all “Active Users” and not those who have made a purchase which is perfect for focusing on engaged users who haven’t converted yet. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/compound-filters) 📲 **Link in-app messages to one-time push notifications:** Create a seamless experience by connecting a one-time push directly to an in-app banner. For example, send a push about an abandoned cart and instantly show an in-app message with a special discount when the user taps. It’s an easy way to grab attention and boost re-engagement. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push/#define-what-happens-when-the-push-is-clicked) 🎨 **Customize in-app message flow:** You now have more control over how in-app messages appear and behave. Choose position, animations, swipe gestures, and close button style to make messages feel more natural and aligned with your app’s design. **✨ New ready-made email templates:** Launch campaigns faster with templates designed for common marketing scenarios, including seasonal events like Black Friday and Christmas. ##### Improvements 🕒 **See send times at a glance:** Scheduled send times are displayed right on the canvas for all active journeys, making it easier to plan, review, and adjust your campaigns. ⚡ **Create faster with the new quick-access button:** The new **Create** button brings your most common actions into one simple menu: start a journey, send a push or email, launch an in-app message, build a segment, or import users. **📅 Filter events by exact dates in Segment Builder:** You can now create segments by filtering users who triggered an event *on*, *before*, *after*, or *between* specific calendar dates. This gives you precise control for time-sensitive campaigns, such as targeting users who made a purchase during a holiday sale or signed up last week. 📈 **Export users who achieved journey goals:** Download a CSV of users who completed a specific goal to analyze performance, launch follow-up campaigns, or create new segments based on conversions. ##### Fixes 🛠️ Fixed an issue where dynamic content did not render correctly in test emails.\ 🛠️ Resolved a visual display problem in the email builder for a smoother editing experience. ## September 2025 Parallel journey session controls, tag-based journey splits, analytics breakdowns, and faster segment launches. ##### What’s new **🔁 Control how many journey sessions a user can have at the same time:** A new setting lets you choose if each user can join just one journey or several in parallel. Pick the single session option for simple, non-overlapping flows like onboarding or subscription renewal reminders. Choose multiple sessions for scenarios like orders, bookings, or appointments, ensuring each user session is tracked separately and messaging stays relevant. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-how-many-sessions-a-user-can-have-at-the-same-time) **🔀 Split journeys by user tags:** Easily create personalized experiences for different groups of users, all in the same journey. With our new **Condition Split by Tag** feature, you can build up to 10 separate branches based on user tags like subscription type, city, or language. For example, you can customize onboarding for different plans, send local promotions, or message users in their preferred language. *Note: This feature currently supports string tags only.* [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/#split-flow-by-tag) ##### Improvements **📊 Analytics dashboard: breakdown by messages and journeys:** You can now slice metrics by individual messages or entire journeys to see which creatives or flows drive results. ##### Fixes **🛠️ Optimized segment-based starts:** Segment assembly is now faster and more reliable. This means your customer journeys will launch faster and more consistently, which is especially beneficial for high-volume campaigns. ## August 2025 Best Time to Send for email, API to remove users from journeys, HubSpot & devtodev integrations, new analytics filters, messaging controls, and fixes. ##### What’s new **📧 Best Time to Send is now available for emails!** Stop the guessing game — we’ll automatically deliver your emails to each user exactly when they are most likely to open and engage. We analyze past behavior to find the optimal delivery time, helping you boost open rates and get better results from every campaign. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#enable-best-time-to-send) **🗑️ New API method: Remove users from journeys.** Easily keep your journeys clean and up to date. With our new *Remove Users from Journeys* API method, you can instantly remove users from a specific journey or from all journeys at once. [Learn more](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/drop-users/) ##### Integrations **🔗 HubSpot integration:** Effortlessly connect your HubSpot CRM with Pushwoosh to sync contact data and automate personalized messages based on real-time customer activity. Send push notifications directly from a contact’s page or trigger them automatically through HubSpot workflows. [Learn more](https://docs.pushwoosh.com/product/integrations/hubspot-integration/) **🔗 Devtodev integration**: Our new **devtodev integration** makes it easy to move behavioral cohorts straight into Pushwoosh with just one click. You can instantly launch personalized push, in-app, email, or SMS campaigns to the right users, no manual work needed. [Learn more](https://docs.devtodev.com/3rd-party-sources/cohort-export/pushwoosh) ##### Improvements **📊 Get a clearer view of user behavior:** We’ve added **Total performed events** and **Unique users** filters to your analytics dashboards. Now you have the flexibility to analyze user actions exactly how you want. Whether you’re tracking the total volume of events or focusing on the number of individual users performing an action, you can build more precise and insightful reports to inform your strategy. **⚙️ Control your one-time messages with more precision:** You can now set capping and send rate limits directly in the **Sending time** step for one-time messages. This gives you better control over delivery speed and helps reduce user fatigue. ##### Fixes **🛠️ Journey list**: Improved the display when no journeys are available for a smoother experience. **🛠️ In-app messages**: Enhanced scheduling options with clearer date and time selection. ## July 2025 Auto-selection for A/B/n test winners, new trigger-based entry options, channel-specific rate limits, improved project insights, and enhanced journey stability. ##### What’s new **🚀 Auto-selection for A/B/n test winners:** Tired of manually checking your A/B/n tests? Let us handle it! Our new feature auto-selects the winning branch in your customer journeys. All you have to do is set a goal, and we’ll track the performance of each path. Once a clear winner is found, all new users are automatically routed to the best-performing branch. This means your journeys are always optimized for success, without any extra work from you. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#choose-how-the-winner-will-be-selected) **🗓️ Trigger-based journey entry limits:** Control exactly when users can enter your journeys. Set a specific date range so entries are only allowed during that window. This is perfect for time-sensitive campaigns like a summer sale or product launch, ensuring your messaging stays relevant and timely. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#set-entry-availability) **👥 Trigger journeys for users from event attributes:** Ever wanted to trigger a journey for a customer who didn’t perform the action themselves? Now you can! We’ve made it possible to start journeys for other users by simply passing their ID as an attribute in an event. This opens up a world of possibilities for your journeys, like automatically rewarding a referrer when their friend signs up, or sending a gift recipient a welcome journey after a purchase. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#define-who-enters-the-campaign) **🚦 Take control with channel-specific rate limits:** We know that one size doesn’t fit all when it comes to communication. That’s why you can now set individual send rate limits for channels like push notifications, emails, and SMS. This allows you to fine-tune the delivery speed for each channel independently. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/send-rate-limits/) ✉️ **List-Unsubscribe with custom URL:** You can now add a custom unsubscribe URL to the **List-Unsubscribe** header when sending emails via the API. This enables native **Unsubscribe** buttons in email clients like Gmail, helping reduce spam complaints and protect your sender reputation. ##### Improvements **🔁 More transparency when restarting campaigns:** When resuming a paused scheduled journey, you’ll now get a clear overview of how audience segments will behave. This helps you restart campaigns with confidence. **📊 Improved Project Overview panel:** The updated Project Overview gives you a clear snapshot of your app’s activity, including key audience metrics and active channels. Quickly access message creation and project settings to launch campaigns faster and manage projects more efficiently. ##### Fixes **🛠️ Journey category renaming:** Renaming journey categories is now seamless! We fixed a bug so all of your changes update instantly in the journey list, without any delay. **🛠️ Even better platform stability:** We’ve made several backend improvements to increase the overall reliability and performance of customer journeys. ## June 2025 AI-powered segmentation, journey change history, multi-channel vouchers, Magento integration, smarter scheduling, language-based stats, and more. ##### What’s new 🧠 **AI-powered segmentation:** Say goodbye to manual segment creation! With AI-powered segmentation, you can generate user segments using simple natural language prompts. Just describe your audience, like *“Users who haven’t opened the app in the last 30 days”*, and the AI will create the segment for you. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai) ⏳ **View journey change history:** Need to know who made changes to a journey? Use the new **History** panel, located on the right side of the journey builder, to track all updates. You can see what was changed, when it happened, and who made the change. This ensures full transparency and gives you better control over version history. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-editing/#view-journey-change-history-and-versions) 🎟️ **Vouchers go multi-channel:** Great news! Vouchers can now be delivered via email, SMS, LINE, WhatsApp, and Data to App, alongside existing push notification support. Enjoy more versatile and engaging campaigns with expanded reach. ##### Integrations 🛍️ **Magento integration:** We’re excited to announce that Pushwoosh now integrates with Magento, a leading e-commerce platform, allowing you to automatically sync customer data, orders, and abandoned carts. This enables you to engage users based on their store activity and drive more conversions. [Learn more](https://docs.pushwoosh.com/product/integrations/magento-integration/) ##### Improvements 📊 **New journey stats:** Quickly access all key metrics for your journey in one place to gain better insights and improve performance tracking. 📱 **Redesigned WhatsApp element:** We’ve made our WhatsApp element even easier and more intuitive to use, streamlining your communication setup. 📆 **Smarter scheduling for journeys:** Ensure no user is left out. Decide what happens if a user’s scheduled time has passed: include them immediately, the next day, or skip them entirely. 🌍 **Language-based message stats:** Now you can view message performance statistics by language version. In the Message Stats panel, simply switch between language versions (e.g., English, German, French) to view delivery rates, opens, and errors for each one separately. 👀 **Push content preview in Customer Journeys:** Now you can see exactly how your push message will look before it’s sent. The new preview feature lets you check the content directly from the journey builder to make sure everything looks just right. ⚡ **Faster segment calculation for events:** Event-based segments now process faster thanks to backend optimizations, helping you work more efficiently and get results quicker. 🔁 **Improved migration to Pushwoosh:** We’ve streamlined the migration process to make moving to Pushwoosh easier and more efficient, with fewer manual steps and better overall performance. ##### Fixes 🛠 **Better language display in journeys:** Cleaned up how languages show up in Customer Journey, ensuring a clearer and more consistent experience. 🛠 **Smoother element interaction:** Fixed a bug that made interacting with journey elements feel slow or unresponsive. 🛠 **Element renaming fixed:** You can now rename journey message elements without any issues. 🛠 **Faster loading in User Explorer:** We’ve sped up data loading in the User Explorer so you can access user info more quickly. ## May 2025 Personalized promo codes with Voucher pools, LINE messaging support, Shopify and Event Streaming integrations, streamlined Web push configuration, and more. ##### What’s new **🎟️ Send personalized promo codes:** Effortlessly send unique promo codes with Voucher pools. Upload a list of codes and Pushwoosh will automatically insert a unique one into each personalized push message. Ideal for discounts, referrals, or one-time-use offers. This feature is currently available only in Customer Journey Builder. [Learn more](https://docs.pushwoosh.com/product/content/vouchers/) 📱 **Reach more users with LINE messaging:** Expand your audience and boost engagement by sending messages through LINE, one of the most popular messaging apps! Pushwoosh now offers full support for LINE via both [API](https://docs.pushwoosh.com/developer/api-reference/line-api/) and Customer Journey, giving you more powerful ways to connect with your users and enhance your messaging strategy. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/) ##### Integrations **🛒 Shopify integration:** You can now connect your Shopify store to Pushwoosh! Use synced customer data to personalize messaging, boost engagement, and increase conversions. [Learn more](https://docs.pushwoosh.com/product/integrations/shopify/) **🚀 Introducing Event Streaming integration:** Gain deeper insights and improve efficiency with our new Event Streaming integration. Now you can receive structured batches of communication event data in near real-time. With advanced filtering, platform targeting, and reliable delivery, this integration is ideal for powering analytics, automation, and data-driven decisions. [Learn more](https://docs.pushwoosh.com/product/integrations/event-streaming/) ##### Improvements 🌐 **Streamlined Web push configuration:** Setting up web push just got simpler and faster! We’ve introduced a unified configuration process for all supported browsers (excluding older Safari versions). This means a more intuitive and efficient experience for getting your web push notifications up and running. [Learn more](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/web-push-configuration/) **📥 Smarter Tag management for CSV uploads:** Importing tag values is now faster and easier. Now you can quickly select or deselect all tags when uploading a CSV, saving time and simplifying bulk tag updates. ##### Fixes 🛠️ Resolved several issues affecting push content editing and display for a smoother message creation experience. ## April 2025 AI-powered message translation, simplified email subject A/B testing, improved message stats, enhanced Device API security, a revamped AI composer, refined CTR metrics, and more. ##### What’s new **🌍Introducing AI-powered translation for seamless multilingual campaigns:** Expanding your global reach just got simpler! Our new **Translate with AI** feature enables you to automatically translate your push notification messages into your selected target languages. [Learn more](https://docs.pushwoosh.com/product/content/push-presets/push-ai-composer/#translate-with-ai) ✨**Smarter subject line testing:** We’ve eliminated the hassle of duplicating presets just to test subject lines. Now, simply add two email elements with the same underlying content but different subjects directly in your Journeys. This empowers you to easily run A/B tests, identify winning subjects, and unlock higher open rates with minimal effort. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-the-subject-line) 🚀 **Explore Aggregated message statistics for deeper insights:** Get a clearer understanding of your messaging performance with the redesigned Aggregated Messages statistics page. Analyze delivery and error metrics grouped by message sets to identify trends and troubleshoot issues more efficiently. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/) **🔒Device API now secured with token authentication:** To strengthen security and access control, the Device API methods now require authentication using a dedicated **Device API token**. [Learn more](https://docs.pushwoosh.com/developer/api-reference/api-access-token/#api-access-token-types) ##### Improvements **🤖Redesigned AI push composer:** Our AI-powered push content composer boasts a redesigned, intuitive interface. Effortlessly create targeted push messages with relevant keywords, leveraging optional fields for tone, audience, and goals to generate highly personalized content in moments. **📈 More precise push CTR calculation:** We’ve refined how Push CTR is calculated to provide you with more balanced and insightful campaign performance data. CTR is now measured on a per-message basis and then averaged. Additionally, we’ve incorporated device-level engagement to give you a more accurate picture of user interaction with your push notifications, particularly when multiple messages are sent in a single day. **📊 Enhanced data visualization:** Incompatible filters are now automatically disabled in charts, guaranteeing more accurate and clearer data insights. ##### Fixes **🛠 Improved customer journey canvas responsiveness:** Bug fixes now provide a smoother building experience. ## March 2025 Instant in-apps, segment-based messaging, and journey cloning across projects, plus smarter roles, clearer push presets list, and smoother customer journeys. ##### What’s new **📲 Instant in-app messages**: Want to show a special offer right after a user adds an item to their cart? Now you can — no need to build a full journey. Instant in-apps let you display messages the moment a specific event occurs, reaching users while they’re actively engaged. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) **📩 Send messages directly from Segments:** Now you can quickly reach your audience right from the Segments list, without switching to the Customer Journey builder or one-time messages. **🔁 Journey cloning between projects:** Want to test a journey in a sandbox before going live? Now you can clone it between test and production environments — keeping all logic, segments, and content intact. It’s also an easy way to reuse high-performing campaigns across multiple apps. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#clone) ##### Improvements **👥 Smarter user roles and invitations:** Inviting users and assigning roles is now simpler and more intuitive, giving you better control over team access and permissions. **🗂️ More informative push presets list:** The presets list now displays key configuration details upfront, making it quicker to review and select the right template for your campaign. ##### Fixes **🧭 Improved customer flow in journeys:** Users now continue seamlessly through journeys even after their User ID changes (e.g., after login), reducing drop-offs and ensuring smoother automation. ## February 2025 Event import via CSV, redesigned push stats, message tracking in User Explorer, improved conversion goals, and flexible one-time targeting. **What’s new** 🚀 **Effortless event management**: Want to import events from your analytics and CRM systems into Pushwoosh? We’ve made it simple! Import event data with CSV files in just a few clicks. Events are automatically linked to users in User Explorer, giving you accurate tracking and deeper user behavior insights. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#importing-events) 📩 **Simplified message tracking**: The new **Received messages** tab in User Explorer lets you quickly search, find, and review every message sent to a user—making tracking and analysis a breeze! [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-explorer/#received-messages-tab) 📊 **Smarter push analytics**: We’ve redesigned push statistics to give you clearer, more actionable data on delivery, engagement, and user interactions. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#view-detailed-push-notification-statistics) **Improvements** **🔔 Enhanced notification settings in push presets:** Configure the number of unread notifications shown on the app icon (badge count), sound, and delivery priority directly within push presets — no extra steps needed. **🎯 Flexible platform selection for one-time messages:** Now, when sending one-time messages, you can target any platform of your choice! Easily select specific platforms like iOS, Android, Chrome, Safari, or Firefox to ensure your message reaches the right audience. **📈 Improved conversion goal calculation:** Get a clearer picture of your campaign’s performance with improved conversion goal calculations. Enhanced accuracy means better insights—helping you optimize strategies and drive results with confidence. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) **🤝 Faster, more intuitive user & role management:** Team collaboration just got easier! We’ve redesigned the user invitation and role management flow to make adding users and assigning roles faster and more intuitive. **Fixes** 🌍 **Multi-language support issue resolved**: We’ve addressed a rare issue affecting multi-language functionality, ensuring consistent performance across all languages. ## January 2025 Improved tag statistics with historical data, refined email open rate calculations, enhanced Journey reliability with batch processing, simplified Silent Hours setup, and expanded message visibility in User Explorer with more details and quick stats access. **📊 Tag stats just got better:** Tag statistics are now even more powerful! We’ve added historical data, updated weekly, allowing you to track performance trends over time and make more informed decisions. **📧 Improved email open rate calculation:** We’ve updated email open rate calculations to provide a clearer, more accurate reflection of how your emails performed. **🚀 Enhanced Journey reliability:** Journeys are now more reliable and stable thanks to the implementation of batch processing. This improves message handling and ensures smoother execution. **⏰ Easier Silent hours setup:** The Silent Hours settings now feature a more intuitive interface, allowing you to easily configure message delivery pauses. Set specific time ranges for each day or pause messages for an entire day if needed. 📨 **Improved message visibility in User Explorer:** Get more insight into your messages! The Received Messages section now provides expanded message details, and you can quickly access statistics by clicking on the message code. ## December 2024 Streamlined Journey tracking, improved push notification presets, enhanced email analytics, and more. ##### What’s new * **New Journey metric:** The new “Still Waiting” metric shows the number of users currently waiting in **Time Delay** or **Wait for Trigger** steps. Use this data to identify bottlenecks and optimize your Customer Journeys for improved efficiency and performance. * **Platform-specific deep links in push presets:** Easily add platform-specific deep links to your push notification presets. This allows you to tailor user experiences based on their device platform (iOS, Android, etc.). ##### Improvements * **Enhanced email statistics:** New, detailed statistics provide deeper insights into your email campaigns. This helps you analyze performance more effectively, identify trends, and maximize campaign success. * **Customize From email addresses in Journeys:** Gain greater flexibility in your messaging by customizing the From-email address directly within each Journey. This allows you to align sender addresses with specific campaigns, improving message consistency and clarity. ## November 2024 Simplified WhatsApp messaging, enhanced one-time push and email tools, extended storage for events and message history, new default events for better user interaction tracking, and more ##### What’s new * **WhatsApp messaging, simplified:** Connect with your customers on WhatsApp effortlessly. [Link your Pushwoosh account to your Facebook Business account](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration) and start sending personalized messages from the Pushwoosh customer journey. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages) * **Enhanced one-time push:** One-time push is back and better than ever! Our new form makes it a breeze to send instant notifications or schedule them for later. Perfect for urgent alerts or time-sensitive campaigns. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/one-time-push#how-to-send-a-push-notification-using-the-one-time-push-form) * **One-time email, revamped:** Remember our one-time email tool? We’ve refined it to make it even more powerful. Easily send single emails without the hassle of building a full campaign. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/send-emails/send-one-time-emails/) * **Extended storage for events and message history:** Events and message history are now stored for a full year! This upgrade allows you to analyze communication and campaign data over longer periods, track user behavior trends, and gain deeper insights for more effective campaigns. * **New default events for enhanced tracking:** We’ve introduced three new default events (**PW_EmailOpen, PW_InAppShown, and PW_InAppClicked**) to provide more granular data on user interactions. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_emailopen) ##### Improvements * **Improved Testing experience:** The Test Devices section has been upgraded to provide a more robust testing experience. You can now view device HWIDs and add email and SMS test devices to ensure the accuracy of your campaigns. * **Default Platform tag:** A default **Platform Tag** is now created automatically for all new projects — no manual setup is required. * **Streamlined Live Activities integration:** Live Activities integration is now simpler than ever with a default implementation that works out of the box, making it easier to set up real-time updates in your app quickly and efficiently. * **User-specific Timezone tag for new accounts:** To enhance personalization and engagement, we’ve made the Timezone Tag user-specific to all new accounts. This means the timezone is unified across all of a user’s devices, ensuring messages are delivered at the optimal time based on their local timezone. * **Update User profile:** Numerical Tags are now supported for user profiles, simplifying the management of quantitative data and enabling more precise audience segmentation and analytics. ##### Fixes * Resolved push notification duplication for accurate message delivery. * Fixed missing opened date for received messages in User Explorer. ## October 2024 Modal Rich Media for unobtrusive content display, CSV exports for campaign stats, detailed user journey data, enhanced user path analysis and more ##### What’s new * **Introducing Modal Rich Media:** Enhance user engagement with Modal Rich Media, a new media format that displays content without taking over the full screen—position content at the top, bottom, or center for a flexible, unobtrusive experience. Learn more about [Android](https://docs.pushwoosh.com/developer/pushwoosh-sdk/android-sdk/android-modal-rich-media) and [iOS](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-modal-rich-media) implementations. * **Export campaign statistics in CSV format:** Take control of your campaign performance with detailed CSV exports. Analyze key metrics for a selected date range to identify trends and make informed decisions for better results. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#export-campaign-statistics) * **Export user data for each step in Customer journey:** Gain deeper insights into user behavior by exporting detailed user lists for each step of your customer journey. Analyze user interactions with push notifications, in-app messages, emails, and more to optimize campaign performance and engagement. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/#export-statistics-for-each-step-of-your-customer-journey) * **Analyze detailed user paths:** Get a comprehensive understanding of user behavior by tracking and analyzing individual user paths within your campaigns. Visualize each step users take in their customer journey to identify opportunities for optimization and enhance the overall user experience. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/) ##### Improvements * **Streamlined email configuration flow:** Easily set up your email channel with our new streamlined configuration flow. * **In-depth user insights:** The User Explorer now provides detailed insights into individual user activity within campaigns and segments over the past two months. Track user journeys, join dates, statuses, and segment participation to better understand user behavior, improve engagement, and refine targeting. * **Auto-save for email content (Beta):** Our new Auto-save feature automatically saves your email templates every 3 seconds. Enable it now in Save preferences within the Email editor. * **Insert and reuse email content easily:** Simplify your email creation process! Now you can easily insert one piece of email content into another, reusing elements like headers, footers, or specific blocks across multiple emails. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/drag-and-drop-email-editor#insert-and-combine-email-content-blocks) * **Real-time segment size calculation and platform counters:** Pushwoosh now offers instant segment size estimation as you refine targeting conditions, saving you time. Additionally, you can now view subscriber counts for each platform (e.g., iOS, Android, Web) to optimize targeting. You can also export this data for in-depth analysis. * **Preview your email campaign with test emails:** Ensure your email campaign looks its best before sending. Send a test email to ensure your message looks great and make adjustments as needed. Learn more * **Improved UX for journey elements:** We’ve refined the journey element interface to make your workflow more efficient and enjoyable. ##### Fixes * **Faster user profile loading in User Explorer:** The User Explorer now loads user profiles significantly faster, providing quicker access to detailed user insights. * **Resolved duplicate display of rich media on Android devices:** We’ve resolved an issue that caused Rich Media content to sometimes display twice on Android devices. ##### Integrations * **Webhook integration for real-time email status updates:** Our new Webhook integration provides real-time email status updates, helping you stay informed on email performance. Use this feature to respond quickly to changes and refine your email strategies for optimal results. ## September 2024 Save messages to mobile inbox, easy-to-create SMS presets, pre-built email templates, automatic segment calculation, and more. ##### What’s new * **Save messages to mobile inbox:** Now, you can save messages to the app’s inbox directly from the Journey canvas. This allows users to access and review important messages at their convenience. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/save-to-inbox) * **Easy-to-create SMS presets:** Streamline your campaigns with reusable SMS presets. Tailor your messages to diverse audiences, translate them into multiple languages, and personalize them for maximum impact. [Learn more](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets) * **More insights with platform counters in Segments:** You can now view platform-specific counters, displaying the number of subscribers on each platform within a Segment. This provides detailed insights into segment composition and helps optimize your targeting strategy. * **Pre-built email templates:** We’ve introduced ready-made email templates for common use cases like abandoned carts, appointment reminders, order confirmations, and re-engagement. These templates are easily editable and customizable to suit your specific needs, helping you save both time and effort in creating campaigns. ##### Improvements * **Segmentation logic cloning for streamlined campaigns:** When using Segmentation Language in your API queries, you don’t have to start from scratch. You can simply copy the Segmentation Language from an existing segment. * **Automatic segment calculation**: The actual number of subscribers in a segment is now automatically calculated upon creation. If you modify the segment conditions, the number will be recalculated instantly, ensuring accuracy and up-to-date data. Learn more * **Streamlined email platform configuration:** Enjoy a more efficient and user-friendly email configuration process with the updated flow, designed to simplify and speed up setup. * **Save email content anytime:** In the email builder, the **From** and **Reply-To** fields are now optional, allowing you to save your email content at any time, whether your email platform is fully set up or not. This gives you greater flexibility when preparing your campaigns. * **Save email content blocks for future campaigns:** Improve your email marketing workflow by saving content blocks as you design your emails. These blocks can be easily reused in future campaigns, helping you save time and maintain consistency in your messaging. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/emails/drag-and-drop-email-editor#save-content-blocks-for-future-use) * **Set a test device in User Explorer:** You can now easily mark devices as test devices in the User Explorer section of the Pushwoosh Control Panel. This allows for simplified campaign testing. * **Unified content categories:** You can now ensure consistent content management across all communication channels within a project by organizing all content types under unified categories. ##### Fixes * Addressed layout issues in the **Push element** in Customer Journey to improve user experience. * Resolved formatting issues in the **Time Delay** journey element for smoother navigation. * Fixed an issue where journeys would not properly restart based on segment conditions after a pause. ## August 2024 Global frequency capping for better control over message frequency, personalized birthday and anniversary campaigns, a new “Data to app” journey element and more. ##### What’s new * **Introducing Global frequency capping:** Gain greater control over message frequency across your campaigns. Prevent message fatigue and maintain user engagement by setting limits on how often users receive messages. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) * **Birthday and anniversary campaigns:** Create personalized campaigns that celebrate your customers’ birthdays, anniversaries, or other significant milestones. By segmenting users based on specific date tags, you can send targeted automated messages, such as birthday greetings or exclusive offers, to nurture customer loyalty and boost engagement. [Learn more](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-anniversary-campaigns/) * **New Data to app journey element:** Expand your app’s capabilities with the new Data to App element. Send custom data or silent push notifications to deliver information that your app can interpret and act upon. This feature enables personalized content, in-app actions, and background updates without disrupting the user experience, enhancing engagement and overall app functionality. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) ##### Improvements * **Live Activity for user segments via API:** You can now start Live Activities for specific user segments directly through the API. Use this feature to broadcast real-time updates to your audience, such as football match results, or provide real-time countdowns for upcoming events, ensuring that your users are always informed and engaged. * **Improved push token management:** We’ve optimized push token updates in our Android SDK. Tokens will now refresh more frequently, even in the background, ensuring reliable delivery. * **Improved documentation experience within the Control Panel:** Helpful articles and guides now conveniently slide out from the right side of the screen, ensuring quick and easy reference while maintaining focus on your current task. ##### Fixes * Resolved an issue that prevented the correct display of email templates in the legacy email template. ## July 2024 Enhanced security with authentication app, new dashboard filters, improved scheduled launch, easier referral program sign-up, and more ##### What’s new * **Global frequency capping**: This new feature allows you to regulate the number of messages sent to a user through different channels within a specific time frame. By avoiding message overload, you can improve the user experience and maximize the effectiveness of your campaign. * **New journey element: Data to App:** Send custom data to your app with the new Data to App Journey element. Send silent pushes, deliver promo codes, update app content, and create a more engaging user experience directly from the Journey canvas. * **Dashboard filters:** Dive deeper into your data by applying filters for platforms, push IDs, email IDs, audience segments, campaigns, or aggregated messages. Tailor your analysis and make data-driven decisions with greater precision. * **Enhanced security with authentication app:** We’ve added an extra layer of security! You can now use an authentication app like Google Authenticator to generate verification codes for faster, more secure logins. ##### Improvements * **Improved Scheduled Launch:** We’ve simplified the Scheduled Launch setup in Audience-based entry journeys. The future’s new, user-friendly interface allows you to effortlessly set launch dates and recurring intervals, giving you precise control over your campaign timing. Plus, you can now deliver messages at the perfect time for each subscriber by launching journeys based on their device timezone. * **Reason for journey pause:** View the exact reason for your journey’s deactivation or pause directly in the journey list. * **Easier Pushwoosh referral program sign-up:** Now you can join our referral program directly from the Pushwoosh control panel in just a few clicks. ##### Fixes * **Enhanced autosave:** Your journey changes are now saved more reliably, preventing accidental data loss when you leave the Journey canvas. * **Return button fix:** We’ve fixed issues with the return button on the Journey canvas, making navigation smoother. ## June 2024 Improved push creation form, enhanced conversion goals tracking with up to 30-day periods, new integration with Piano Analytics for detailed user insights and more. ##### What’s new * **Streamlined push creation:** No more toggling between tabs! Our new push content form lets you craft the perfect message and configure settings directly within the customer journey. This all-in-one approach simplifies push creation, saves time, and allows you to reuse push content for future campaigns. [Learn more](https://docs.pushwoosh.com/product/content/presets) * **Improved conversion tracking:** Get a more complete picture of your campaign performance! Now you can set a conversion period (up to 30 days) to capture customer actions after they’ve interacted with your campaign. This captures delayed conversions, giving you valuable insights beyond just immediate clicks. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings#specify-the-conversion-period) ##### Integrations * **Piano Analytics integration:** This integration unlocks detailed user behavior insights, empowering you to create precise segments and deliver highly targeted omnichannel campaigns. Reach the right users at the right time, and boost user engagement and revenue. [Learn more](https://docs.pushwoosh.com/product/integrations/piano-analytics-integration) ##### Fixes * **Accurate deactivated journey status:** Deactivated journeys in the Campaigns list now display their correct status. * **Silent hours fix:** Silent hours configurations are accurately reflected for better control over notification schedules. * **More accurate SMS stats:** We’ve fixed an issue affecting SMS campaign statistics. Now, your data is more accurate and reliable, helping you optimize your marketing efforts. ## May 2024 Revamped Campaigns list for journey insights, centralized Account Settings page, granular metric breakdowns, BCC for journey emails, new integrations, and more ##### What’s new * **New Campaigns list:** Get a quick view of all your customer journey data! The new Campaigns list shows dedicated cards for each journey, providing key details and performance metrics for the entire journey and individual messages in one place. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journeys-list/) * **New Account Settings page:** The new Account Settings page is your one-stop shop for managing login credentials, security settings, time zone, and notification preferences. Plus, gain valuable insights into your Pushwoosh usage metrics – all designed to enhance account control and security. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/) * **Metric Breakdowns for granular insights:** You can now add breakdowns of key metrics to your dashboards and get a segmented view of your data. This unlocks a deeper level of analysis, empowering you to make data-driven decisions and refine your strategies with greater precision. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/) * **BCC (Blind Carbon Copy) in customer journeys:** You can now turn on the BCC option when sending emails in a customer journey. Ideal for recording interactions in your CRM or keeping internal teams informed. The feature is available upon request. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#turn-on-bcc-blind-carbon-copy) * **New API method for creating iOS Live Activities:** Enable real-time updates and interactions within your iOS applications with this new method, enhancing user engagement and providing a more dynamic user experience. [Learn more](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api#startliveactivity) ##### Integrations * **RevenueCat integration:** Send targeted push notifications, in-app prompts, and other messages based on subscription events from RevenueCat and boost engagement and revenue. [Learn more](https://docs.pushwoosh.com/product/integrations/revenuecat-integration/) * **WebViewGold integration:** WebViewGold allows businesses to convert their websites into mobile apps for iOS and Android without coding. As a next step, WebViewGold users can now leverage Pushwoosh to initiate powerful omnichannel communication, enhancing engagement and effectively reaching their audience. [Learn more](https://docs.pushwoosh.com/product/integrations/webviewgold-integration/) ##### Fixes * **Segment settings issue resolved:** We’ve fixed an issue that caused segment settings to reset when adding segments directly to journeys. Your segment configurations will now be saved correctly, ensuring a seamless journey-building experience. * **Smoother cloning for deactivated journeys:** Previously, when cloning a journey with a deactivated schedule, the same setting would carry over, preventing the new journey from starting. This issue is now fixed, enabling the successful cloning of deactivated journeys. * **Journeys with deleted presets now pause automatically:** To prevent unintended campaign behavior, journeys relying on deleted presets will now automatically pause. This ensures your campaigns continue using only valid configurations. * **Improved string tag handling:** String tags in the Webhook journey element are now formatted correctly, ensuring your webhooks transmit data smoothly and efficiently. ## April 2024 Pushwoosh Affiliate Program, simplified Customer Journey creation, landscape Rich media content for captivating experiences, new Retention tab, and more * **Introducing the Pushwoosh Affiliate Program:** Become a Pushwoosh affiliate partner and boost your earnings by promoting a reliable and cost-effective solution backed by excellent onboarding support. [Learn more](https://www.pushwoosh.com/products/affiliates/) * **Simplified Customer Journey creation:** Streamline your workflow and save time with our simplified journey creation process. Get started quickly with ready-made templates for common scenarios or build journeys from scratch for your unique needs. [Learn more](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project) * **Landscape Rich media content:** Create powerful rich media content specifically designed for horizontal viewing. This is perfect for tablets, apps in landscape mode, and more. Ensure your campaigns offer a smooth and visually captivating experience on all devices, keeping users engaged. [Learn more](https://docs.pushwoosh.com/product/messaging-channels/in-apps/no-code-in-app-editor#creating-portrait-and-landscape-rich-media) * **All-new Retention tab:** Pushwoosh Statistics is now enhanced with retention rates: user retention curve and cohort charts await you. Dive deeper into how users interact with your app over time and analyze the effectiveness of your retention strategies. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics) ##### Integrations * **New Integrations page:** Explore our library of third-party integrations and seamlessly connect the services you need to boost your campaigns. [Learn more](https://docs.pushwoosh.com/product/integrations/) ##### Improvements * **Rich media audience export:** Export detailed CSV files with user data (HWID, platform, and interactions) straight from the Audience tab. Gain richer insights and optimize future Rich Media content for maximum impact. ##### Fixes * **Cloning points in Customer Journeys:** No more messy duplicates! Cloned points now have unique names, and branch settings in Wait for Trigger points are preserved during cloning. This ensures a smoother workflow and saves you valuable time when managing customer journeys. * **Customer Journey editing:** We’ve fixed a problem where the **Launch again when resuming the campaign** option was automatically enabled when pausing journeys for editing. This used to result in users receiving duplicate messages when journeys resumed. To prevent this, the option is now disabled by default. This ensures smoother and more accurate journey management. ## March 2024 Enhanced Journey control and customizable event storage time ##### What’s new * **Improved Journey control:** Manage your journeys right from the canvas: edit, clone, rename, or stop your campaigns by selecting the desired action from the status menu. Enjoy a more transparent and intuitive workflow. * **Customisable storage time for events:** Control how long you store events data! Now, you can tailor the storage duration of your events to your needs. [Reach out to our sales team](https://www.pushwoosh.com/demo/?utm_source=docs&utm_medium=post&utm_campaign=api-reference_campaigns) for more information. ## February 2024 New pre-built and custom Dashboards, simplified email creation, integration with Qonversion, introduction of Customer Journey Categories, and deeper Customer Journey Insights ##### What’s new * **Pre-built Dashboards:** Gain instant insights into your campaigns with our new app, push, in-app, email, and event dashboards. Customize them to fit your needs and optimize your campaigns with ease. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/) * **Custom Dashboards**: Build your own dashboards to focus on the data that matters most. Make data-driven decisions and optimize your strategies effectively with key metrics at your fingertips. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) * **Simplified email creation:** Say goodbye to tab-switching! Craft content and [configure email setup](https://docs.pushwoosh.com/product/messaging-channels/emails/set-up-your-email-in-the-editor) within a single, user-friendly editor. Choose between the [Drag & drop editor](https://docs.pushwoosh.com/product/messaging-channels/emails/drag-and-drop-email-editor), perfect for those without coding skills or the [HTML code editor](https://docs.pushwoosh.com/product/messaging-channels/emails/html-code-editor) for more advanced control. This streamlined workflow empowers you to create emails faster and easier. ##### Integrations * **Qonversion integration:** Integrate Pushwoosh with Qonversion to boost retention and maximize revenue. Use Qonversion subscription data to create personalized campaigns like renewal reminders and custom offers for users who canceled their subscriptions. [Learn more](https://docs.pushwoosh.com/product/integrations/qonversion-integration) ##### Improvements * **Deeper Customer Journey insights at your fingertips:** We’ve upgraded Customer Journey statistics, introducing new metrics and making them available directly on the Journey canvas. This means you can understand your journey’s performance at a glance, without having to spend a lot of time analyzing each step. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/) * **Customer Journey categories:** Say goodbye to the clutter in your campaign list! Create up to 10 custom categories to organize your journeys and filter your journeys by category or status for effortless navigation. * **Enhanced control for Customer Journeys:** Before deleting a journey step or removing a connection between steps, you’ll now receive a warning highlighting the potential loss of associated data. This will help you make informed decisions and avoid unintended consequences. ## January 2024 Message Inbox enhancements, better Customer Journey insights, automatic Tag and Event creation, improved personalization, and more ##### What’s new * **Message Inbox:** Now, you can schedule push messages to automatically disappear from the inbox after a set number of days, ensuring your inbox remains up-to-date. For example, you can send a promo code valid for a week and set it to vanish from the inbox after 7 days. Use Message Inbox in automated customer journeys and stay sure that your messages are timely and relevant. * **Enhanced Customer Journey insights:** Statistics for in-apps and rich media sent from Customer Journey are now displayed on the journey canvas. You don’t have to switch from Journey to reports, resulting in a much smoother workflow. ##### Improvements * **Automatic Tag and Event сreation:** For Applications created after July 2023, Events will be automatically created along with their attributes when using the /postEvent and /setTags API methods. If you need additional attributes, you can easily add them through the Pushwoosh interface. * **Enhanced personalization:** You can now personalize Deep Links, Custom Data, and Inbox images when creating push notifications. For example, you can use Dynamic Content in the Deep link URL to provide different links for different languages. This feature enables you to create more customized and engaging experiences for your audience. ##### Fixes * **Faster Rich Media loading:** Rich Media content used in Customer Journeys now loads faster, providing a smoother user experience. ## December 2023 Revamped Billing Page, smarter Silent Hours, enhanced Event Segmentation, streamlined onboarding, a redesigned Application statistics dashboard and more. ##### What’s important * **Revamped Billing Page:** Gain a comprehensive view of your billing information, including payment methods, upcoming payment schedule and history, and estimated payment amounts. Plus, enjoy the flexibility to make invoice payments at your convenience. [Learn more](https://docs.pushwoosh.com/product/account-management-and-security/billing-page) ##### What’s new * **Smarter Silent Hours:** Now you have more control over silent hours for each channel. Set custom times and define how messages are handled during those periods. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#silent-hours) * **Enhanced Event Segmentation:** You can now select users who haven’t triggered specific events within your chosen timeframe. ##### Improvements * **Speed Boost:** The control panel now loads faster, saving you precious seconds. * **Streamlined Onboarding:** We’ve optimized Pushwoosh’s onboarding process with additional prompts to deliver relevant support and tips right when you need them. * **Trust This Device:** After initial two-factor authentication, you can now enable the Trust This Device feature, allowing you to skip sending the authorization code for 30 days. * **Redesigned Statistics:** We’ve revamped the Application statistics dashboard, formerly known as Audience Statistics, and relocated it to the Statistics section, for intuitive navigation and enhanced clarity. * **Tame Complex Journeys:** Enhance the visual structure and readability of complex, multi-step campaigns with the new Simplify Layout button. [Learn more](https://docs.pushwoosh.com/product//customer-journey/journey-settings/#simplify-the-journey-layout-for-better-readability) ##### Fixes * **Campaign Search:** The search in the Customer Journey campaigns list is now case-insensitive, so no more typos slowing you down! * **Refined Two-Factor authorization:** Tweaked two-factor authorization for uninterrupted logins and reliable code delivery. ## November 2023 In-App message expiration dates, enhanced user experience to prevent accidental Segment deletion in Customer Journeys, improved email campaign performance tracking, and more. **November 20 | Customer Journey: Control the lifetime of your in-app messages** Now you can set the expiration date for your in-app messages, ensuring that your messaging stays timely and relevant. For example, you can offer a promo code valid for three days after a user completes a purchase. Simply set a three-day expiration period for the in-app message containing the promotion, and you’re good to go. ##### November 10 | UX improvement: Ensuring you don’t accidentally delete segments used in Customer Journeys We’ve made sure you won’t disrupt any ongoing campaigns by deleting a segment used in them. Now, if you attempt to delete an audience Segment linked to a Customer Journey, you’ll receive a warning that you need to remove it from the Customer Journey first. This step prevents you from deleting Segments by accident or messing up any ongoing campaigns. ##### November 3 | Project Overview: Get a clearer picture of your email campaign performance Dive deeper into your email campaign performance with the enhanced Project Overview section. Now you can track metrics like email soft bounces, hard bounces, and complaints across all email messages for each app. To streamline your analysis, you can filter the data by period. ##### November 2, 2023 | Project Overview: Streamlined time period logic Tracking statistics for specific periods is now more convenient, thanks to enhancements in the Project Overview section. When you select “This week” or “This month” in Detailed statistics, you’ll now see data for the current calendar week starting from Sunday or for the current calendar month. This change provides a clearer representation of your campaign performance over the current week/month, helping you make more informed decisions. ## October 2023 Improved Campaign performance tracking by Channel, enhanced Event search, granular insights into Custom Rich Media performance, the new Conversion Funnel for visualizing user journey conversion rates, and more. ##### October 26 | Project Overview: Analyze your campaign performance by channel at a glance We’ve improved the Project Overview section to give you a clear and focused view of your campaign performance across different channels. Now, you can effortlessly track channel-specific metrics for emails, push notifications, and in-app messages. We’ve also enhanced the graph visuals to make it easier to identify trends at a glance. To top it off, we’ve removed the Total Audience and Audience by Platforms metrics that used to confuse many of our users, allowing you to focus on the metrics that matter. ##### October 14 | UI update: Search and track Events with ease We’ve revamped the Events screen to make it easier for you to stay on top of your events. Now, you can quickly search for specific events, view their attributes, and track how often they’ve occurred in the past seven days. This is a significant improvement from the previous version that had a long list of events with no search functionality and only showed the total number of times an event had been triggered. ##### **October 13 | Unlock granular insights with custom Rich Media stats 📊** Now you can dive deeper into your custom Rich Media performance and track user engagement metrics like clicks on links and buttons, and form submissions. This granular data, previously only available for Rich Media created with the built-in editor, empowers you to refine your Rich Media strategy and drive better results. ##### October 12 | Introducing Conversion Funnel: Visualize conversion rates at each stage of the user journey Gain deeper insights into how a user behaves after receiving a push notification with Pushwoosh Conversion Funnel. This new feature visually maps the user journey, allowing you to track conversion rates at each stage and optimize your messaging strategies to drive better results. ##### **October 10 | Unlock the power of personalized messaging with the Segment integration** Our new integration with Segment empowers you to deliver truly personalized messages that resonate with your audience. Segment is a customer data platform that simplifies user data collection and utilization. Once you connect Segment and Pushwoosh, you can integrate your customer data into Pushwoosh and craft messaging campaigns tailored to each user’s unique context, preferences, and behaviors.\ [Learn how to use data for deeper personalization](https://blog.pushwoosh.com/blog/personalized-communications-segment-integration/) ## September 2023 New Rich Media statistics, A/B/n test optimization in Customer Journey Builder, a new Default Event, personalization using Liquid Templates in Customer Journeys, integration with Amplitude, and more. ##### September 26 | New Rich Media statistics Meet the new Rich Media statistics: now, you can analyze the performance of each Rich Media page by viewing key metrics, visual graphs, and tables. Detailed statistics will provide valuable insights for improving your Rich Media pages for future campaigns and increasing conversion rates. [More about Rich Media statistics](https://docs.pushwoosh.com/product/content/rich-media/#rich-media-statistics) ##### September 14 | A/B/n tests in Customer Journey Builder: Keep only the winner Now, you can instantly turn off less-performing branches once a statistically significant winner is identified. This will help you optimize the campaign’s effectiveness without pausing it. After clicking **Keep only winner**, new users in the journey will be directed to the winning branch, while those who already entered deactivated branches will continue the journey through them. ##### September 14 | New Default Event: PW_NotificationSend We have added a new Default Event: **PW_NotificationSend**. It lets you track whether a message is sent, regardless of the channel used. You can use it to create segments based on the number of messages sent and analyze user behavior after sending a message to them. [More about the PW_NotificationSend Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_notificationsend) ##### September 12 | Customer Journey Builder: Liquid Templates personalization You can now use [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) to personalize push notifications, emails, and SMS in customer journeys. This will give you more flexibility when setting up message personalization. For example, you can add a list of purchased items and their price in the email text instead of a single value. ##### September 12 | Customer Journey Builder: Enhanced Wait for Trigger step We have enhanced the Wait for Trigger step in the Customer Journey Builder: * Now, you can create up to three branches with different conditions. For example, this will allow you * You can now select the AND operator when adding multiple events to one branch. If the AND operator is used, only users who meet all the conditions specified in the branch will proceed further. [More about the Wait for Trigger step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) ##### September 8 | Drive targeted engagement with the new Amplitude integration** We’re excited to announce our integration with Amplitude, a powerful analytics platform that helps track and understand user behavior better. Now you can harness the insights gained from Amplitude to deliver relevant and personalized campaigns through Pushwoosh. ##### September 5 | Segment integration: now in public beta** Pushwoosh’s integration with Segment is now in public beta, empowering you to harness the power of personalized customer data for impactful messaging campaigns. [Learn more about the integration](https://docs.pushwoosh.com/product/integrations/segment-integration) ## August 2023 Integrations with AppsFlyer and Segment, new Default Events, Segment Insights for precise message analysis, a no-code Rich Media (In-App) editor, and more. ##### **August 30 | Enhance your messaging with Segment integration (Private beta)** We’re thrilled to announce the private beta of our integration with Segment, a customer data platform designed for effortless user data collection and utilization. This powerful partnership enables you to seamlessly connect your Segment data to Pushwoosh and craft personalized messaging campaigns that resonate with your audience. [Learn more about the integration](https://docs.pushwoosh.com/product/integrations/segment-integration) ##### August 18 | Cloning Segments Now, you can quickly create a new segment based on an existing one using the **Clone segment** option. This will save you time when building Segments with similar conditions. ##### August 18 | Message statistics: Segment Insights View message statistics for selected user segments on the **Segment Insights** tab. This will help you analyze and improve your communication approach based on the most accurate data. [More about Segment Insights](https://docs.pushwoosh.com/product//statistics-and-analytics/messaging-statistics/) ##### August 16 | New Default Events: PW_DeviceUnregistered, PW_EmailLinkClicked We have added two new Default Events: * **PW_DeviceUnregistered** fires when the app is uninstalled or a user opts out of emails or push notifications. Use it to retain users when they decide to stop using your app or unsubscribe from one of your channels. * **PW_EmailLinkClicks** fires every time a user clicks a link in an email. Use it to run targeted campaigns for users who click or don’t click on a specific link in an email. You can use these Events out of the box by simply activating them in your project. [More about Default Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceunregistered) ##### **August 15 | Elevate your campaigns with AppsFlyer integration** We’re excited to unveil our integration with AppsFlyer, a mobile attribution and marketing analytics platform. This powerful partnership empowers you to harness the power of AppsFlyer data to craft personalized and effective campaigns in Pushwoosh. [Learn more about the integration](https://docs.pushwoosh.com/product/integrations//appsflyer-integration) ##### August 10 | No-code Rich Media (In-App) editor Meet the new built-in editor that allows you to easily create Rich Media pages, including In-Apps, without coding. Add headers, texts, images, buttons, forms, and other elements to the canvas and customize them in just a few clicks. Using deeply customized In-Apps will help you increase user engagement even more. [More about in-app editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) [](https://docs.pushwoosh.com/docs-release-notes-14.webm) ##### August 8 | Customer Journey Builder: Webhook-based Entry Now you can start a journey whenever you need by sending an API request. This may come in handy if you want the journey to launch automatically once some business event occurs outside the app. For example, you can inform customers when some of your products are back in stock. In the request, you can specify segmentation conditions and modify message content. [More about Webhook-based Entry](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) ## July 2023 Enhanced A/B/n Testing, mParticle Integration, and Targeted Scheduling with Silent Days in Customer Journey Builder ##### July 26 | Customer Journey Builder: Improved A/B/n tests We have significantly improved the functionality of the A/B/n Split element. Now, you can get detailed and reliable analytics for your tests, including conversion to selected goals and metrics for calculating statistical significance. [More about running A/B/n tests in customer journeys](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) ##### July 10 | mParticle integration mParticle users can now send customer data to Pushwoosh and create personalized campaigns in Pushwoosh based on this data. To set up the integration, please [follow this guide](https://docs.pushwoosh.com/product/integrations/mparticle-integration). ##### July 7 | Customer Journey Builder: Silent Days When configuring Silent Hours in the Customer Journey Builder, you can now also specify the weekdays when sending must be paused. Users will only receive messages and continue their journey after the *Silent Hours + Silent Days* period ends. For example, this can be useful if you don’t want to disturb your users on weekends. ## June 2023 Customer Journey Builder enhancements: Editing active journeys and Optimal Time to Send feature, plus a user-friendly welcome screen and Get Started section for streamlined project configuration. ##### June 22 | Getting started with Pushwoosh: First steps update We have added a welcome screen and the Get Started section to make it easier for new users to configure their first project. ##### June 20 | Customer Journey Builder: Editing active Journeys Now you can edit any elements and settings of a Customer Journey that has already been launched. A Journey must be paused while editing and can be re-launched at any moment. This new feature will allow you to improve your communications and adjust active campaigns to changing needs. [More about editing Customer Journeys](https://docs.pushwoosh.com/product/customer-journey/journey-editing/) ##### June 13 | Customer Journey Builder: Optimal Time to Send We added the [Optimal time to send]() option to the Customer Journey Builder: now Pushwoosh can automatically choose when to send a push to each user in the journey based on their behavior and previously sent messages’ performance. This will help you make your communications more effective. To enable this option, please get in touch with our [Sales team](https://www.pushwoosh.com/demo) or your dedicated Customer Success Manager.‌ ## May 2023 Sorting Message History by key metrics and a new “Recipients by Platform” graph in Project Overview ##### May 24 | Message History: Sorting by metrics You can now sort the [Message History](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history) list by *Recipients*, *Opened*, *CTR*, and *Errors* metrics. This lets you quickly find messages that perform best or require your attention. ##### May 23 | Project Overview: Recipients by Platform We added the [Recipients by Platform](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#user-distribution-by-platforms) graph to the Project Overview section. It will give you a clearer view of how recipients are distributed across selected platforms. ## April 2023 Improved Time Delay in Customer Journey Builder, User Explorer update, SMS in Customer Journey, Segment filters by event attributes and more. ##### Apr 27 | Time Delay: Setting specific dates and days of the week We have improved the [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) element in Customer Journey Builder: now you can set the specific date and time to continue the journey. You can also select a particular day of the week. Use this option to quickly set up one-time campaigns for specific dates without manually calculating the time delay. ##### Apr 21 | User Explorer update The **Messages History** tab in User Explorer has been renamed to **Received Messages**.‌ ##### Apr 21 | SMS in Customer Journey You can now add [the SMS channel](https://docs.pushwoosh.com/product/messaging-channels/sms/) in the Customer Journey Builder. To use this feature, please contact our [Sales team](https://www.pushwoosh.com/demo/?utm_source=docs&utm_medium=post&utm_campaign=release-notes) or your dedicated Customer Success Manager. ##### Apr 18 | Message History update We have updated the **Message History** section: search and filters are now more user-friendly, and the table contains more information about each message. The **Debug Mode** option has been renamed to **API messaging tracing**. ##### Apr 18 | Segment filters by event attributes In the [Segments (Filters)](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation) section, you can now build a segment based on a specific attribute of the selected event. ##### Apr 13 | RFM Segmentation without the Monetary attribute In the [RFM Segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation) section, you can now build a behavior-based segment without the Monetary attribute. To do this, select “none” as a value of the Monetary attribute, and the segment will be calculated based on Recency and Frequency only. ##### Apr 13 | Drop Offs statistics in Customer Journey Now you can see detailed statistics on Drop Offs in the [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/): the reasons for drop-offs and the number of drop-offs for each cause. This will help you identify and fix problems at each point of the Journey. The feature is available to all Customer Journey Builder users. ##### Apr 11 | Average CTR in the Journeys list You can now view the Average CTR for each Journey in the Journeys list. ##### Apr 7 | New SMS channel We’ve added the SMS channel to Pushwoosh: now you can set up sending SMS using the Pushwoosh API. Use SMS to contact customers who are not reachable via other channels, send transactional notifications, and make marketing offers. [Learn more about sending SMS](https://docs.pushwoosh.com/product/messaging-channels/sms/) --- # Getting started with statistics & analytics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/ Track, analyze, and optimize your campaigns with Pushwoosh analytics tools to gain insights into user behavior and engagement. --- Pushwoosh offers a robust suite of tools to track, analyze, and optimize your campaigns. Gain valuable insights into user behavior, messaging performance, and overall project success to refine your communication strategy and drive better results. This section provides all the essential information to measure, evaluate, and improve the performance of your campaigns. [Project overview ](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) [Track unique users, retention, push audience, email performance, and conversions, and email the statistics on a schedule with auto-reports.](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) [Journey statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/) [Measure user progress, conversions, and drop-offs across customer journeys to fine-tune your workflows.](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/) [Dashboards ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) [Access visual analytics for a comprehensive overview of your project’s performance metrics.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) [Messaging statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/) [Analyze performance data for push notifications, emails, in-apps, and SMS messages.](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/) [Message history ](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) [View historical records of sent messages to analyze past performance and trends.](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) [Aggregated messages ](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/) [Track delivery, engagement, and errors across campaigns grouped from multiple one-time messages.](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/) [User retention statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics/) [Track user retention and understand how campaigns impact engagement over time.](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics/) [Uplift report ](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/) [Measure a single campaign's or journey's impact against its own holdout, with significance and revenue.](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/) [Statistics FAQ ](https://docs.pushwoosh.com/product/statistics-and-analytics/statistics-faq/) [Find answers to common questions about Pushwoosh analytics and reporting tools.](https://docs.pushwoosh.com/product/statistics-and-analytics/statistics-faq/) [Metrics glossary ](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/) [Understand key metrics and terms used across Pushwoosh statistics for accurate analysis.](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/) --- # Aggregated messages **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/ Aggregated Messages tracks message performance in one view, showing delivery, engagement, and errors across your campaigns. --- The **Aggregated messages (campaigns)** section provides performance statistics for communications grouped into campaigns. This helps you monitor how your messages perform over time and analyze the performance of your messaging strategy. You can add [one-time push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#link-to-aggregated-campaign-optional) or [emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#link-to-aggregated-messages) to a campaign yourself, from their own send flow. Once messages are in, their total performance is tracked in one campaign view. ## Create a campaign 1. Go to **Statistics → Aggregated messages**. 2. Click **Create**. 3. Enter a name and description for your campaign. 4. Click **Apply** to save your changes. ![Create campaign dialog with fields for campaign name and description](https://docs.pushwoosh.com/statistics-aggregated-messages-2.webp) After the campaign is created, you can [assign one-time emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#link-to-aggregated-messages) or [push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#link-to-aggregated-campaign-optional) to it. Pushwoosh will automatically track and display the combined performance of all assigned messages in [**Message stats**](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#message-stats). ## View and manage campaigns On **Statistics → Aggregated messages**, the list shows each campaign’s **Campaign name** and **Description**. Use **Search** to find a campaign by name. ![Aggregated messages list with Search, Create, Campaign name and Description columns, and a three-dot menu on each row](https://docs.pushwoosh.com/statistics-aggregated-messages-list.webp) To manage a campaign, click the **three-dot menu (⋮)** next to the campaign name, then choose: * **Open:** View detailed performance metrics and all associated messages. * **Messages Log:** Access a full list of communications included in the campaign. * **Edit:** Update the campaign’s name or description. * **Delete:** Remove the campaign from your dashboard. Note Deleting a campaign removes its combined stats. The individual messages it contained, and their own data, aren’t affected. ## Message stats To open **Message stats** for a campaign, click its name in the **Aggregated messages** list. The dashboard always shows **Overview**, **Errors**, and **Timeline**. **Events** (push) or **Clicks** (email) appear only after the campaign has sent something on that channel. Note If the campaign covers more than one channel, a second row of tabs appears above the Message stats tabs. Choose the channel first (for example **iOS, Android** or **Email**). Everything below that row updates for the channel you picked, including whether you see **Events** or **Clicks**. ![Message stats with a channel tab row above Overview, Events, Timeline, and Errors](https://docs.pushwoosh.com/statistics-aggregated-messages-channel-tabs.webp) ### Overview tab Use the **Overview** tab to check lifetime reach, opens, and where recipients dropped off. On email, you also see delivery, clicks, and unsubscribes. #### Campaign performance These cards summarize how the campaign performed for the channel you are viewing. Hover the **i** icon on any card for the exact percentage formula. ![Overview tab of a campaign's Message stats, showing the Audience, Sent, and Opened performance cards](https://docs.pushwoosh.com/statistics-aggregated-messages-overview-tab.webp) | Card | What it shows | | :--------------- | :---------------------------------------------------------------------------------------- | | **Audience** | Who you targeted for this campaign, and how many stayed eligible after exclusions. | | **Sent** | Share of **Audience** that the delivery side accepted for sending. | | **Opened** | Share of **Sent** on push, or of **Delivered** on email, that opened at least once. | | **Delivered** | Email only. Share of **Sent** that did not bounce. | | **Clicked** | Email only. The same total the [Clicks tab](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#clicks-tab) breaks down link by link. | | **Unsubscribed** | Email only. Share of delivered recipients who unsubscribed or marked the message as spam. | Note These cards, and the funnel below them, always count the campaign’s entire history. Only the [Timeline tab](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#timeline-tab) lets you pick a date range. Once a card has recipients, use its **three-dot menu (⋮)** to **Export list** or **Create segment**. Details are in [Export recipients and create a segment](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#export-recipients-and-create-a-segment). #### Campaigns distribution **Campaigns distribution** shows the same lifetime numbers as the cards, broken into four steps: * **Audience:** who you targeted * **Sent:** who was accepted for sending * **Deliveries:** who got a delivery confirmation * **Opened:** who opened 1. Open the view dropdown in the section’s top-right corner. 2. Choose **Funnel view** (default) or **Table view**. ![Campaigns distribution funnel showing Audience, Sent, Deliveries, and Opened stages, each broken down into its own drop-off reasons](https://docs.pushwoosh.com/statistics-aggregated-messages-campaigns-distribution-funnel.webp) ##### How to read the funnel * The percentage on a step is the share that reached it from the step above. * The list under a step shows how those recipients split (for example accepted vs rejected). * If a row is missing, nobody fell into that group for this campaign. | Stage | What it shows | | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Audience** | Who you targeted, then who was left out. **Any channel:*** [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) (**Excluded: frequency capped** = unique recipients skipped before send) * [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) * filtered by category**Email only:*** unsubscribed in a previous campaign * bounced in a previous campaign * complained in a previous campaignPush never shows those three rows. | | **Sent** | Whether the message was taken for delivery or not.* **Accepted by gateway:** Apple, Google, or another platform took the push into work. On email, this is **Accepted by sender service**: the provider took the send into work. Neither means the message reached the device or inbox. * **Rejected by gateway:** the platform rejected the send (for example a token or configuration problem). On email: **Rejected by sender service**. Details are on the **Errors** tab. * **Processing errors:** Pushwoosh failed before the gateway. | | **Deliveries** | **Push only.** Whether alerts were on at send time, and whether the device confirmed delivery. **Note:** A silent push that confirms still counts as a delivery. | | **Opened** | - **Push:** one Opened total, no further split - **Email:** confirmed opens, machine-only opens (for example Apple Mail Privacy Protection), and unknown opens | Note **Push:** If any push in this campaign is older than 30 days, **Deliveries** for the whole campaign shows only confirmed / no confirmation, even for pushes you sent today. To find the old message, open **Messages Log** (see [View and manage campaigns](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/#view-and-manage-campaigns)). For the full breakdown on one message, see [Detailed push notification statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#detailed-push-notification-statistics). **Email:** If any email in this campaign is older than 60 days, **Opened** still shows a total, but without the confirmed / machine-only / unknown split. That total includes machine-only opens indistinguishably from human opens, so do not compare or add it to a younger campaign’s split. For the full split on one message, see [Detailed email statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#detailed-email-statistics). The age that triggers this is the campaign’s oldest message. A push you sent today still loses its Displayable/AMPP split at the campaign level if an older message shares the campaign. ![Campaign-level Deliveries funnel collapsed to confirmed and not-confirmed rows for the whole campaign](https://docs.pushwoosh.com/statistics-aggregated-messages-11.webp) ### Events tab The **Events** tab only appears for a campaign’s push channel. Use it to see how often custom app events fired after the campaign’s pushes went out, beyond opens tracked automatically. ![Events tab listing custom events with their Hits and Unique counts and a per-row export icon](https://docs.pushwoosh.com/statistics-aggregated-messages-events-tab.webp) | Column | What it shows | | :--------- | :---------------------------------------------------------------------------------------------------------------------------------------------- | | **Hits** | How many times the event fired. | | **Unique** | How many unique users triggered it at least once. This is an estimate, not an exact recount, so don’t expect it to match an export row for row. | Click the **download icon** next to an event to export it. ### Clicks tab The **Clicks** tab only appears for a campaign’s email channel. It lists every link in the campaign’s emails with its click count, sortable by clicks. * To highlight a link on the click map, select its row in the list. * If a link has extra tracking parameters, expand the row to see clicks by parameter value. * To export who clicked a link, use the **download icon** next to it. #### Click map Use the click map next to the link list to see where people clicked in the email. Darker links got a larger share of the clicks. On a campaign, the map pools clicks from every message in the campaign onto that one preview. ### Timeline tab Use the **Timeline** tab to see how the campaign performed over a date range, not only as lifetime totals on **Overview**. The chart and the table under it track the same four metrics: * **Accepted by gateway:** the delivery platform (Apple, Google, or another) took the push into work. On email, this is **Accepted by sender service**: the provider took the message into work. * **Rejected by gateway:** the platform rejected the send (for example a token or configuration problem). On email: **Rejected by sender service**. * **Delivery confirmations:** the device, platform, or mailbox provider reported delivery. This is not the same as the person seeing or opening the message. * **Opens:** someone opened the push or email. ![Timeline tab showing a Metrics over time chart with Accepted/Rejected by gateway, Delivery confirmations, and Opens, plus a per-period table below it](https://docs.pushwoosh.com/statistics-aggregated-messages-timeline-tab.webp) To configure the chart: * Set the time range with **All time** or a preset window. * Use **Compare metrics** to show a subset of the chart’s lines. * Use **All platforms** to keep every platform, or pick one platform to filter the chart and table. These controls apply only to the chart and the summary table. They do not change the Overview cards or funnel. Note **These numbers count messages, not people.** A subscriber reached by several of the campaign’s messages is counted once per message, not once for the whole campaign, here and in the Overview cards and funnel alike: the campaign-level funnel is a sum of each message’s own funnel. **Accepted** and **Rejected** land on the send day. **Delivery confirmations** and **Opens** land on the day the device or mailbox responded, which can be hours or days later. So the four lines in one time bucket are not a funnel the way the **Overview** funnel is. ### Errors tab Use the **Errors** tab to see why sends were rejected. See [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) for what the tab shows, and [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) for what each error code means. ### Copy the campaign code To copy the campaign ID: 1. At the top of the page, click **Campaign code**. 2. The ID is copied to your clipboard. Pass it as the `campaign` parameter in [`messages:list`](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) when you need the campaign’s messages from the API. You usually do not need it to read the stats. ### Export recipients and create a segment #### Export recipients Exporting recipients requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. To download recipients as CSV from the top of the page: 1. Click **Export stats**. 2. Confirm the download. You can also click **Export list** from a card’s three-dot menu (⋮) on **Overview**, or from an error row on the **Errors** tab. **Export stats**, and **Export list** from those two places, use the same per-recipient log, windowed to the last 30 days: anyone older is missing from the download. An event or link row on the **Events** or **Clicks** tab (download icon) exports separately, covering the campaign’s whole history, not just the last 30 days. The export controls appear only after the campaign has sent at least one message. #### Create a segment You can create a segment from **Sent**, **Delivered**, **Opened**, or **Clicked** on **Overview**. 1. Open the **three-dot menu (⋮)** on that card. 2. Click **Create segment**. 3. Name the segment and save it. --- # Dashboards overview **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/ Create and customize dashboards in Pushwoosh to track key metrics, analyze audience insights, and optimize your messaging performance. --- [Youtube Video: Create and customize dashboards in Pushwoosh to track key metrics, analyze audience insights, and optimize your messaging performance.](https://youtube.com/watch?v=IZ9C7ckGejc) Pushwoosh provides a flexible and user-friendly Dashboard Builder that allows you to create customized dashboards to suit your business needs. These dashboards can be tailored to display the data that matters most to your business, including information about events, message metrics, audience insights, and more. With your selected metrics available at a glance, you can stay on top of your performance, make informed decisions, and optimize your strategies effectively. ## In this section: [Creating and customizing dashboards ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/) [Create and personalize dashboards to fit your needs.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/) [Building and managing charts ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/charts/) [Add, edit, and visualize data with dashboard charts.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/charts/) [Refine analytics with filters ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/filters/) [Use filters to focus on relevant data.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/filters/) [Gain deeper insights with data breakdowns ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/) [Analyze segmented data for better decision-making.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/) [Dashboard metrics and data retention ](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/) [Learn about available metrics and their retention periods.](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/) --- # Gain deeper insights with data breakdowns **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/ Use breakdowns to analyze metrics by platform, attributes, or tags. Compare user behavior across groups for better decision-making. --- ## Understanding breakdowns Breakdowns help you analyze data by dividing it into smaller groups. For instance, you can break down your data by platform to see how users on iOS compare to those on Android. Unlike filters, which limit the data shown, breakdowns let you see all the data but organized into categories for easier comparison. ## Applying breakdowns To add a metric breakdown, locate the **Breakdown** field at the bottom of your chart and select the metrics you want to break down. Please note that a maximum of five breakdowns can be displayed at once to avoid cluttering the chart. ![Breakdown field at the bottom of a chart showing metric selection options](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-18.webp) Metrics can be broken down by: * **Platforms** - applicable to all data. * **Attribute values** - applicable only for events. * **Tag values** - applicable only for tags. * **Segment** - applicable to Communication and Events metrics, with a few exceptions (see [Segment](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/#segment) below). ### Platforms The following chart breaks down the **PW_ScreenOpen** event by platform, showing iOS, Android, and Huawei. This breakdown helps you understand how users on different platforms interact with your app. For instance, you might discover that iOS users open screens more frequently than Android users. This insight can help you prioritize features or marketing efforts for iOS to capitalize on user engagement. ![Chart showing PW_ScreenOpen event breakdown by platform with iOS, Android, and Huawei](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-19.webp) ### Attribute values This chart displays the breakdown of the **PW_ScreenOpen** metric by the country attribute. It provides insights into user engagement across different regions. Analyzing these trends helps businesses customize strategies and enhance user engagement in specific locations. ![Chart displaying PW_ScreenOpen metric breakdown by country attribute showing user engagement across regions](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-20.webp) ### Tag values The breakdown by the tag attribute provides insights into user behavior based on the different tags assigned to events. Analyzing these trends helps businesses understand how specific tags affect user engagement, enabling more targeted decisions for content and strategy optimization. ### Segment Use the **Segment** breakdown to compare audience segments on one chart. Each selected segment becomes its own series, so you can see how engagement or messaging metrics differ between groups, instead of looking at one combined audience. The breakdown works with Communication and Events metrics (with a few exceptions noted below). To set it up: 1. Add up to **three** [Segment filters](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/filters/#segment) to the chart. 2. Turn on the **Segment** breakdown. Without the breakdown, those filters act as one audience: a user is included if they match **any** of the selected segments. With the **Segment** breakdown on, each segment is its own line on the chart, with its name in the legend. Use this when you want to compare the segments next to each other. Note A segment on this chart is a **cohort**: a fixed set of users who matched the segment when it was last recalculated, not a live list that updates as users join or leave. This is the same idea as a cohort in Amplitude — if that’s a more familiar term, see the [entity mapping](https://docs.pushwoosh.com/product/integrations/amplitude-integration/#glossary-mapping-entity-names-if-they-are-different) in the Amplitude integration doc. To refresh membership, click **Update cohorts** on the chart before you trust the numbers. If a segment still needs recalculation, the chart does not show app-wide totals. It shows a status message with the segment names that need an update. You see the same status in the chart builder and on the saved dashboard card. Some metrics do not support Segment breakdown (for example **Subscribers**, **DAU/MAU**, and **Opens**). For those, the chart returns an error that names the metric. It does not silently switch to app-wide totals. ## Total value You also have the option to include a total value in your chart. This helps you see the big picture of your data and provides a baseline against which individual metrics can be compared. ![Chart with total value option enabled showing overall data baseline for comparison](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-21.webp) --- # Building and managing charts in dashboards **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/charts/ Learn how to build, customize, and export charts in Pushwoosh dashboards to analyze key metrics and optimize your campaigns. --- ## How to build a chart Each dashboard contains charts displaying various metrics, which enables you to gain a comprehensive insight into your campaign and application performance. A dashboard can have up to 10 charts. 1. To add a chart, click **Add Chart** in the Dashboard Builder. ![Dashboard Builder interface showing Add Chart button to create a new chart widget](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-9.webp) 2. In the Chart Widget that opens, provide a descriptive and clear chart name that accurately represents the data it contains. You can always edit the chart name later if necessary. ![Chart Widget dialog with text field for entering a descriptive chart name that represents the data it contains](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-10.webp) ### Selecting metrics In the **Metrics** section, choose the specific metrics you need to monitor. You can select up to five metrics to display in a single chart. This allows for a comprehensive view of multiple data points simultaneously. ![Metrics section in Chart Widget showing dropdown menu with available metrics organized by Communication, Audience, Finance, Events, and Tags groups](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-11.webp) All the metrics available are divided into the following groups: * **Communication Metrics** that offer insights into the performance of various communication channels. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#communication-metrics) * **Audience Metrics** that provide insights into your application’s audience. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#audience-metrics) * **Finance Metrics** that reflect Stripe transaction data (revenue and subscriptions) attributed to your Pushwoosh campaigns. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#finance-metrics) * **Events Metrics** that allow you to understand user behavior by examining event-related metrics. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#events) * **Tags Metrics** that allow you to understand the reach and distribution of tags across your user base [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/#tags) You can mix and match metrics from different groups to tailor your chart to your needs. ### Visualization Dashboard Builder supports several types of visualizations, including: * **Lines** – Ideal for trend analysis over time. * **Bars** – Useful for comparing values across categories. * **Pie** – Best for displaying percentage breakdowns. ![Visualization type selector showing options for Lines, Bars, and Pie chart types with preview of each visualization style](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-16.webp) At the top of the page, specify the period or set a custom period. You can also specify the interval at which your data is displayed: Daily or Hourly for periods up to 30 days and Daily or Monthly for longer periods. For a more in-depth analysis of your data, refer to the [Filters](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/filters/) and [Breakdowns](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/) sections. ## Exporting data You can also export your chart as a CSV file. To do this, click on the three dots in the upper right corner of the report. ![Chart menu with three dots icon in upper right corner showing export option to download chart data as CSV file](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-17.webp) --- # Refine analytics with filters **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/filters/ Use filters to refine analytics by platform, campaign, or segment, helping you focus on the most relevant data for better insights. --- ## Understanding filters Dashboard filters allow you to narrow down the data you see by specifying certain conditions. For example, you can filter by platform (like iOS or Android), by specific campaigns, or by user segments. This helps you focus on the data that matters most to you, excluding any irrelevant information. ## Applying filters To add filters, first click on the **Plus icon** in the **Filters** section. ![Filters section with Plus icon button to add new filter options including Platforms, Push, Email, Campaigns, Aggregated Messages, and Segment](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-13.webp) Then, select a filter from the list of filter options, including: * **Platforms:** Filter data by different platforms such as iOS, Android, Huawei, etc. * **Push:** Filter out metrics and events achieved with specific push notification(s). * **Email:** Filter out metrics and events achieved with specific email(s). * **Campaigns:** Filter data by different campaigns (journeys). * **Aggregated Messages:** Filter different aggregated messages. * **Segment:** Filter metrics from the Communications and Events metric groups for users in the selected segments. Important Filters may not be applicable to all metrics. The categories of metrics each filter supports are specified in their respective descriptions. Make sure to check filter compatibility when analyzing data. Note That compatibility check aside, filters combine as follows: if multiple filters are selected from the same group, they are applied using an OR logic. Filters from different groups, however, are applied using an AND logic. This means that events must meet the criteria of all selected filters from different groups, but only one of the selected filters within the same group needs to be satisfied. ### Platforms This filter allows you to focus on data from specific platforms, such as iOS, Android, Huawei, and others. It helps you understand metrics on different devices and operating systems, enabling you to optimize your communication strategies accordingly. **Applicable to:** all metrics except DAU, MAU, and Finance metrics ### Push This filter enables you to analyze the performance of individual push messages. You can track key metrics such as delivery rate, open rate, and user interactions for each notification. This helps in identifying which push notifications resonate most with your audience, allowing for more targeted and effective messaging in the future. **Applicable to:** communication metrics and events Note The Push Filter is specifically applied to conversion events that are triggered by push notifications. These events are defined and counted based on the guidelines in the documentation, which can be found [here](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#custom-events-after-a-tap-on-this-push). ### Emails Use this filter to examine the performance of specific email campaigns. It provides insights into open rates, click-through rates, and conversion metrics, helping you evaluate the success of your email marketing strategies. You can also compare different email campaigns to determine which content and subject lines are most effective. **Applicable to:** communication metrics ### Campaigns This filter allows you to analyze data for specific campaigns, which can include multiple types of communications (e.g., push notifications, emails, in-app messages). Campaigns help you track user interactions across a sequence of messages designed for a particular goal, such as onboarding, retention, or re-engagement. **Applicable to:** finance metrics, communication metrics, and events ### Aggregated Messages This filter provides an overall view of messaging performance by aggregating statistics from multiple communications. Unlike campaigns, which focus on structured message sequences, aggregated messages give a high-level summary of all sent communications within a selected timeframe. This helps in assessing the overall reach and engagement of messaging efforts. **Applicable to:** communication metrics and events ### Segment This filter allows you to narrow down data by predefined audience segments. These segments can be based on user behavior, demographics, or other custom attributes. By applying this filter, you can better understand how different groups of users engage with your content, helping you to tailor your messaging strategies to meet the unique needs of each segment. **Applicable to:** communication metrics and events Before you create a chart with the **Segment** filter, recalculate the segment so the data is current. Click **Update cohorts** on the chart. If the selected segment isn’t prepared yet, the chart shows a status message instead of data. Recalculation can take about 1 minute per 1 million users. To compare several segments as separate lines on the same chart instead of combining them, use the [Segment breakdown](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/breakdowns/#segment). ### Event attributes For a finer event view, filter by event attributes. Select the event (for example, *CheckoutSuccess*), then click **+ Add attribute**. Choose an attribute such as *date* or *product_category*, pick a condition (eq, gte, any, not set, and so on), and select a value if needed. ![Event attribute filter interface showing Add attribute option with condition dropdown and value selection](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-12.webp) ## Examples of using filters ### Compare metrics of different journeys With Dashboard Filters, you can compare the performance of different journeys using various metrics, both overall and for selected messages. For example, if you want to compare the push notification click-through rates (CTR) of two different journeys, follow these steps: 1. In the **Metrics** section, select **Communication - Push CTR**. 2. In the **Filters** section, select **Campaigns**. Choose the journeys you need from the list of customer journeys that opens. You can choose the entire journey or specific messages. 3. Click **Apply**. ### Measure the impact of your campaigns on user-triggered events To understand how your communication efforts correlate with specific events triggered as a result of a campaign and to measure the impact of your messages on user actions, follow these steps: 1. In the **Metrics** section, select **Communication - Push sends.** 2. Choose the target event in **Communication - Events.** 3. Filter the data by the campaign you want to track. 4. Click **Apply**. ### Compare metrics for different segments Dashboard filters allow you to analyze how various user segments respond to your push notifications. Insights from this analysis enable you to customize your strategies for specific groups. For example, to compare CTRs of push notifications sent to new users vs all users, follow these steps: 1. In the **Metrics** section, select **Communication - Push CTR** 2. In the **Filters** section, choose **Push** and select the specific push message for analysis. 3. In the **Filters** section, select **Segment** and choose a user segment from the list. 4. Click **Apply**. --- # Creating and customizing dashboards **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/ Learn how to create, edit, and manage dashboards in Pushwoosh to track key metrics, analyze performance, and optimize campaigns. --- ## Dashboards list To gain insights into the performance of your application and campaigns, navigate to **Statistics → Dashboards.** Here, you’ll discover a list of existing dashboards and can create new custom ones. The list provides the names of the dashboards along with their creation dates, making it easy to manage and access your data. To manage a dashboard, simply click the three dots next to its name, and you’ll have access to the following actions: * **Open Dashboard** * **Rename:** Use this option to modify the dashboard’s name if necessary, helping you keep your dashboards organized and up-to-date. * **Delete:** If you no longer require a dashboard, you can use this option to remove it. Please note that this action cannot be undone. ## Preconfigured dashboards By default, Pushwoosh offers a range of pre-built dashboards tailored to various aspects of your campaigns. These dashboards include: * Application Dashboard * Push Notifications Dashboard * Emails Dashboard * In-apps Dashboard * Events Dashboard ![Statistics Dashboards page showing list of preconfigured dashboards including Application, Push Notifications, Emails, In-apps, and Events](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-1.webp) The **Application** dashboard is designed to help you monitor your app’s performance. ![Application dashboard displaying charts with metrics for monitoring app performance including audience and engagement data](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-2.webp) The **Push Notifications** dashboard is designed to help you track how your push notifications campaigns perform. ![Push Notifications dashboard showing charts with metrics for tracking push notification campaign performance and engagement rates](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-3.webp) The **Emails** dashboard allows you to gauge email campaign effectiveness. ![Emails dashboard displaying charts with email campaign metrics including sends, opens, deliveries, and click-through rates](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-4.webp) The **In-apps** dashboard provides insights into in-app message performance, featuring metrics like In-app Impressions and Interactions. ![In-apps dashboard showing charts with in-app message performance metrics including impressions and user interactions](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-5.webp) The **Events** dashboard contains statistics related to Events used in your campaigns. ![Events dashboard displaying charts with statistics related to events used in campaigns and user behavior tracking](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-6.webp) You have the flexibility to customize these dashboards by adding new charts with the metrics you require or removing any that are not relevant to your specific needs. Note If data is unavailable or channels aren’t configured, the dashboard will display empty charts. ## How to create a new dashboard With the Pushwoosh Dashboard Builder, you have the flexibility to create custom dashboards tailored to your specific needs. These dashboards can be built using various data, such as trigger events, communication metrics, audience analytics, etc. To create a new dashboard, click the **Create Dashboard** button located above the list of your current dashboards. ![Dashboards list page with Create Dashboard button located above the list of existing dashboards for creating custom dashboards](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-7.webp) Give your dashboard a clear and descriptive name that reflects its content. This will help you find it later. Don’t worry, you can always rename the dashboard later if needed. Then, click **Create** to proceed. ![Create Dashboard dialog with text field for entering a clear and descriptive dashboard name and Create button to proceed](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-8.webp) This will open the Dashboard Builder, where you can create custom dashboards that consist of charts containing all the necessary metrics. ## How to edit dashboards Editing dashboards is simple; you can easily add new charts or delete existing ones. To delete a chart, click on the three dots next to the chart name and select **Delete**. ![Chart menu with three dots icon showing Delete option to remove a chart from the dashboard](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-22.webp) You can also modify the period displayed in all the charts on the dashboard. [](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-23.webm "Dashboard interface showing period selector at the top allowing modification of the time range displayed across all charts simultaneously") Additionally, the charts are interactive, allowing you to hide data for specific platforms or metrics. To do this, simply click on the platform or metric at the top of the chart, and it will be hidden from view. You can easily bring it back by clicking on the metric again. [](https://docs.pushwoosh.com/statistics-and-analytics-dashboards-24.webm "Interactive chart showing clickable platform and metric labels at the top that can be toggled to hide or show specific data series in the visualization") You can also adjust the size of the charts for better visibility. ## How to count mail scanner opens and clicks By default, dashboard charts don’t count email opens and clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). To count them: 1. Open the dashboard. 2. In the dashboard header, turn on **Show bots**. The switch changes every chart on the dashboard. The switch isn’t saved, so it’s off again the next time you open the page. When you edit a chart in the Dashboard Builder, use **Show bots** in the builder header to count mail scanners in the chart preview. --- # Dashboard metrics and data retention **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/ Learn about key metrics for push, email, in-app, and audience analytics, along with data retention periods for effective tracking. --- ## Metrics to include in dashboards ### Communication metrics These metrics offer insights into the performance of your primary communication channels, including push notifications, in-app messages, and emails. #### Push metrics | | | | :---------------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Push Sends | Number of push notifications sent, including to the users that have limited the display of push notifications | | Push Opens | Number of push notifications that were opened by recipients | | Push Deliveries | The number of mobile push notifications that the Pushwoosh platform has successfully transmitted to the intended recipients’ mobile devices. | | Push CTR | Percentage of recipients who opened the push notification after receiving it. **Metric calculation:** CTR (%) = (Opened / Recipients) × 100. | | Revenue generated | Sum of event revenue values linked to push performance. To populate this metric, send an event with `__amount` and `__currency` attributes and a `msgHash` attribute that holds the push’s message hash. Events without `msgHash` don’t count toward this metric, including `/postEvent` API calls that omit it. This differs from the [Events table](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#custom-events-after-a-tap-on-this-push) on Message stats, which links events to a push by the user’s last click within 72 hours. Pushwoosh converts values to USD and sums them. | #### Email metrics | | | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Email Sends | The number of emails sent to recipients. | | Email Opens | The number of email recipients who opened your emails within the specified period. | | Email Deliveries | The number of emails delivered to recipients. | | Email Open Rate | The percentage of users who opened an email compared to the total number of emails sent. **Metric calculation:** Open Rate (%) = (Email Opens / Email Sends) × 100. | | Link clicks | The number of recipients who clicked on one or more links included in an email. | | Email CTR | The percentage of recipients who clicked on one or more links contained within an email. It indicates the level of recipient engagement with the email’s content and is calculated using the formula:***CTR = [number of clicks / total emails sent] x 100*** | | Email Hard Bounces | The number of emails not delivered because of hard bounces. A hard bounce happens when an email message can’t be delivered permanently because the recipient’s address either doesn’t exist or is invalid. | | Email Soft Bounces | The number of emails not delivered because of soft bounces. A soft bounce happens when an email message can’t be delivered temporarily due to issues like a full inbox or a temporary server problem. | | Email Complaints | The number of users who have reported your message as spam, informed their email service provider of their complaint regarding your email or expressed dissatisfaction in a manner recognized by email providers and clients as a reason for potentially limiting your ability to send messages. | | Email Rejects | Instances where the recipient’s email server declines to accept an email during the SMTP (Simple Mail Transfer Protocol) transaction. Rejections can result from reasons like authentication failure, spam filtering, or policy-based restrictions. | | Email Unsubscribes | The number of recipients followed the Unsubscribe link in that email. Such users are tagged with the **True** value of the Unsubscribed Emails default tag. | #### In-app metrics | | | | ------------ | ---------------------------------------------------------------------------------------------- | | Impressions | The number of times the in-app message was displayed to users during the selected period. | | Interactions | The number of times users engaged with the in-app message, such as button clicks or link taps. | ### Audience metrics This set of metrics offers insights into user behavior and engagement. | | | | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Subscribers | The number of devices with push tokens or emails provided, meaning they are reachable with push notifications, emails, or In-Apps. This number shows how many users you can reach out to with your communications. | | Recipients | Devices with enabled push notification alerts, i.e., those users who see your pushes. | | Daily Active Users (DAU) | The number of unique users that opened the app on a certain day. | | Monthly Active Users (MAU) | The number of unique users that opened the app in the last 30 days of the month. For example, MAU for September covers September 1 to September 30. In a 31-day month, the window starts on the 2nd, so MAU for October covers October 2 to 31. For the current month, the 30 days end yesterday. | | Opens | The number of times the application has been launched on users’ devices as of the date and time specified. | | Installs | The number of application installs, i.e. the number of times a mobile software application has been downloaded, installed, and prepared for use by end-users within a specified time frame. | | Uninstalls | The number of devices for which push tokens were removed from the database due to their invalid or non-existent status, typically occurring when users have uninstalled the application. | | Push Enables | The number of times users have allowed or opted in to receive push notifications on their devices. | | Push Disables | The number of times users have opted out or disabled push notifications on their devices. | | Daily Active Devices (DAD) | The number of unique devices that opened the app on a certain day. | | Monthly Active Devices (MAD) | The number of unique devices that opened the app during a specific month. | ### Finance metrics Finance metrics reflect Stripe transaction data (revenue and subscriptions) attributed to your Pushwoosh campaigns. These metrics are available in the dashboard builder, but data populates only after you connect [**Stripe integration**](https://docs.pushwoosh.com/product/integrations/stripe-integration/). Note When you connect Stripe for the first time, Pushwoosh automatically creates a **Finance Overview** dashboard with pre-configured **Gross Revenue** and **New Subscriptions** charts. You can also add Finance metrics to any existing dashboard at any time. | | | | :---------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Gross Revenue | Total revenue from successful charges processed through Stripe, before refunds and fees. Amounts are shown in the currency of each transaction. Supports breakdown by currency. | | New Subscriptions | Number of new subscriptions created during the selected period. Renewals and status changes of existing subscriptions are not counted. | Data source Finance metrics are populated automatically via the Stripe integration. The data is pulled from Stripe webhook events: * **Gross Revenue** is based on `charge.succeeded` (one-time) and `invoice.paid` (subscriptions) events. * **New Subscriptions** is based on `customer.subscription.created` events. To populate Finance metrics, connect your Stripe account in **Settings → Integrations → Stripe**. ### Events This group includes default events and custom events you’ve set for your app. ### Tags This group includes default tags and custom tags you’ve set for your app. Understanding how long your data is stored is essential for making informed decisions. Metrics have different retention periods depending on their type and whether filters are applied. Below are the two key retention types: ## Default data retention period By default, all metrics are stored for a fixed period, ensuring access to historical trends and performance insights. | **Metric group** | **Storage period** | | ------------------------- | ------------------------------------------------------------------- | | **Communication metrics** | - **Hourly aggregation**: 1 year - **Daily aggregation**: Unlimited | | **Audience Metrics** | 2 years | | **Events metrics** | 1 year | | **Tags metrics** | 1 year | | **Finance metrics** | 5 years | ## Custom retention with filters When applying filters, the retention period may change. Some filtered datasets, especially those with high granularity, might have a shorter retention period, while aggregated data with filters could be retained for longer. | **Metric group** | **With filters** | | ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- | | **Communication metrics** | **Segment**: 40 days by default (if activated). To extend storage, [contact support](https://help.pushwoosh.com/hc/en-us/requests/new) | | **Events metrics** | **Segment**: 1 year by default. To extend storage, [contact support](https://help.pushwoosh.com/hc/en-us/requests/new) | --- # Understanding Customer Journey statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/ Gain valuable insights into your Pushwoosh campaigns with Customer Journey statistics. Monitor interactions and engagement. --- All Customer Journey statistics are available on the Journey canvas. Pushwoosh shows performance data at two levels: * **Journey-level statistics** Use the **Journey statistics** panel on the left side of the canvas to track audience engagement, goal completion, message delivery, and drop-offs across the entire campaign. [Overall journey statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/) * **Element-level statistics** View metrics for individual steps directly on the canvas. Turn on **Show metrics** in [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) or hover over an element to see how each part of the journey performs. [Journey element statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/) To analyze a specific user’s path through the campaign, see [Analyze user path](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/). --- # Analyze user path in a customer journey **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/ Use Find user path on the Journey canvas to track how a specific user moved through a campaign and highlight their completed steps. --- Pushwoosh allows you to track a specific user’s journey within a campaign by highlighting their steps. By analyzing user paths, you can gain a deeper understanding of how users interact with your campaigns and make data-driven decisions to enhance their overall experience. To track a user’s path: 1. **Navigate to the campaign** you want to analyze. 2. Click **Find user path** at the bottom right of the Journey canvas. 3. **Enter a user identifier.** You can search by [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/), HWID, email, phone, or token — any identifier associated with the user whose path you want to examine. Ensure the value you entered is accurate and relevant to the campaign being analyzed. **Find user path** requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. ![Find user path dialog with User ID input field for tracking user journey](https://docs.pushwoosh.com/customer-journey-statistics-14.webp) Pushwoosh will visualize the user’s journey on the canvas, highlighting the steps and messages they have completed. ![Journey canvas displaying highlighted user path showing completed steps and interactions](https://docs.pushwoosh.com/customer-journey-statistics-15.webp) If a user has engaged with the campaign multiple times, you can explore different paths they’ve taken using the path switcher at the bottom of the screen. A user’s path can have several statuses based on user interaction and the journey’s changes. * If the user successfully completed the journey and exited, no special marking is shown in the list. * If the user dropped off, the flow can still be viewed, and the user’s path will be marked with an exclamation mark. * If the journey was modified, the path will remain in the list, but it cannot be displayed for viewing. If the user has not interacted with the campaign, you will receive a notification indicating that the user was not found. Note For **Find user path**, you can only view user interactions and paths from the last two months. Data older than two months is not available. Tip You can also ask your developer team to help you get stats for a specific journey by its ID using the API. [Learn more](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) --- # Journey element statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/ View metrics for each journey element on the Journey canvas and export step-level data to analyze individual Customer Journey steps. --- To display metrics for each journey element on the canvas, open [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) (the gear icon in the bottom-right corner of the canvas) and turn on **Show metrics** at the bottom of the panel. Alternatively, you can hover over the element to view the metrics. [](https://docs.pushwoosh.com/customer-journey-statistics-5.webm "Show metrics toggle displaying real-time statistics on journey canvas elements") ## Audience progress Pushwoosh allows you to keep track of your audience as they progress through the journey with real-time visualization on the canvas. A number on the arrow following each journey element indicates the total number of customers who have completed that particular step. ![Journey canvas showing audience progress numbers on arrows between elements](https://docs.pushwoosh.com/customer-journey-statistics-6.webp) ## Statistics for each communication point You can gain insights into the performance of each communication point through metrics such as: ### Push notifications and email messages | | | | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Goals reached | Indicates the number of users who reached the Conversion Goal at that specific point. | | Opened | Displays the number of times the message was opened by users. | | CTR | Represents the percentage of users who clicked on the message. | | Drop-offs | Represents the number of messages that couldn’t be sent for some reason (e.g., an expired push token or an invalid email address). | | Exited by Conversion goals or Cancel events | Number of users who exited the journey at this point due to reaching a conversion goal or triggering a [cancel event](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#remove-users-from-a-journey-without-counting-a-conversion). | For deeper insights into individual message effectiveness, double-click on a message element on the canvas. A detailed statistics window will pop up, displaying key metrics for the message such as: * number of sent messages * number of opened messages * conversion rate * number of entries * number of drop-offs, * number of clicks, etc. To see the reasons for drop-offs and the number of drop-offs for each cause, expand the **Drop-off** block. ![Detailed message statistics window showing sent, opened, conversion, and drop-off metrics](https://docs.pushwoosh.com/customer-journey-statistics-7.webp) To access even more comprehensive statistics for a message, click **Detailed Statistics** at the bottom of the window. ![Detailed Statistics button at the bottom of message statistics window](https://docs.pushwoosh.com/customer-journey-statistics-8.webp) ### In-apps | | | | ------------- | ------------------------------------------------------------------------------------- | | Goals reached | Indicates the number of users who reached the Conversion Goal at that specific point. | | Drop-offs | Represents the number of in-app messages that could not be displayed to users. | For a more detailed understanding of your in-app message’s performance, double-click on the message element and then, in the window that appears, click on the in-app’s name. This will open up comprehensive message statistics. ### SMS | | | | ------------- | ------------------------------------------------------------------------------------- | | Goals reached | Indicates the number of users who reached the Conversion Goal at that specific point. | | Sent | Indicates the number of messages sent. | ### Telegram | | | | ------------------------------- | ----------------------------------------------------------------------------- | | Sent | Indicates the number of Telegram messages sent. | | Bounced | Indicates the number of Telegram messages that could not be delivered. | | Entries | Indicates the number of users who reached the Telegram element. | | Sent (who received the message) | Indicates the number of users who successfully received the Telegram message. | You can also review **Viewed date range: all time** and click **Export users**. **Export users** requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. ![Telegram element Statistics tab with Sent, Bounced, Entries metrics, and Export users.](https://docs.pushwoosh.com/customer-journey-statistics-27.webp) ### Audience sync For an **Audience sync** element, the statistics view includes: | | | | ---------------------------------------- | ---------------------------------------------------------------------------------------- | | Total entries | Indicates how many users reached this step in the journey. | | Added to Meta audience | Indicates how many users Meta matched and added to the audience you selected. | | Removed from Meta audience | Indicates how many users Meta matched and removed from the audience you selected. | | Skipped (not synced, moves to next step) | Indicates how many users were not synced. They continue to the next step in the journey. | You can **Export users** from this view. The screen also shows which **Meta Ads account** was used for the sync. ![Audience sync statistics with Total entries, Added to Meta audience, Removed from Meta audience, Skipped not synced moves to next step, Export users, and Meta Ads account for sync](https://docs.pushwoosh.com/integrations-meta-ads-integration-11.webp) ### A/B/n test statistics If you conduct A/B/n tests on different elements of your customer journey, you can quickly identify the best-performing variant of each element. ![A/B/n test results showing performance comparison of message variants](https://docs.pushwoosh.com/customer-journey-statistics-9.webp) ### Still waiting The **Still waiting** metric in Pushwoosh Customer Journey applies to **Time Delay** and **Wait for Trigger** steps. It helps monitor user flow, identify delays, and optimize your customer journey setup. #### Wait for trigger Shows the number of users currently waiting to trigger a specific event. These users have entered the step but have not yet fulfilled the required conditions within the defined waiting period. #### Time delay Reflects users still in the waiting period before proceeding to the next step. ## Export statistics for each step of your customer journey You can export users who passed through each step of your customer journey, including elements like push notifications, emails, in-app messages, Wait for Trigger, Condition split, etc. This allows you to analyze the performance of individual campaign elements. To export statistics: 1. Open the campaign you want to track in the **Customer Journey Builder**. 2. Click on the specific journey step for which you want to export statistics. 3. Click **Export statistics**. 4. Specify the **date range** for the data you wish to export and click **Start exporting**. Note For step-level export, statistics can only be exported for the **last two months of activity**. Ensure that your selected date range falls within this timeframe, as older data is not available for export. ![Export statistics dialog with date range selector for journey step data](https://docs.pushwoosh.com/customer-journey-statistics-12.webp) 5. Once the export process is complete, Pushwoosh will display a confirmation message. Click on the file to download the exported data, or choose to export another period if needed. ![Export confirmation message with download link for exported statistics file](https://docs.pushwoosh.com/customer-journey-statistics-13.webp) --- # Overall journey statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/ Track performance, audience, communications, and campaign health for Customer Journey campaigns in the Pushwoosh statistics panel. --- The **Journey statistics** section provides key performance data and insights for your Customer Journey campaigns. Use this section to track user engagement, goal completion, message delivery, and drop-off rates over time. This section includes the following analytics dashboards: * **Performance**: Shows the performance of each goal, including conversion counts and rates. * **Audience**: Displays the number of users currently in the journey and those who have exited. * **Communications**: Breaks down messages sent by channel, such as Push, Email, SMS, In-app, WhatsApp, Kakao, Telegram, Viber, and Facebook Messenger. * **Campaign health**: A high-level summary of campaign activity, including total entrances, goals achieved, and drop-offs. To view detailed stats for any dashboard, click a section name in the left panel. ![Customer Journey statistics dashboard showing performance metrics, audience data, communications breakdown, and campaign health overview](https://docs.pushwoosh.com/customer-journey-statistics-1.webp) You can also view the number of conversion goals configured for your campaign. To see detailed information about each goal, click **Settings** and select **Conversion goals**. ![Conversion goals button highlighted in campaign settings](https://docs.pushwoosh.com/customer-journey-statistics-2.webp) ##### Select a time period Use the **Period** filter at the top of the dashboard to analyze data for a specific time range. By default, the dashboard displays overall statistics from the campaign launch to the present. To view data for a different timeframe, click the dropdown and choose from predefined options (e.g., Today, Yesterday, 7 Days, 30 Days), or set a custom date range. ![Time period dropdown filter showing options for Today, Yesterday, 7 Days, 30 Days, and custom date range selection](https://docs.pushwoosh.com/customer-journey-statistics-3.webp) ##### Manage campaign status Use the **Campaign status** dropdown in the upper-right corner to manage the campaign. Available options include: * **Pause to edit** * **Clone** * **Rename** * **Stop campaign**. You can restore a stopped campaign later to resume editing. ![Campaign status dropdown menu with options to pause, clone, rename, or stop the campaign](https://docs.pushwoosh.com/customer-journey-statistics-4.webp) ## Performance The Performance section shows how effectively your campaign achieves its goals. It helps you evaluate campaign success and identify areas for improvement. Each goal is displayed with two key metrics: * **Conversion rate (%)**: The percentage of users who achieved the goal relative to total campaign entrances. * **Count**: The total number of users who completed the goal. When you click the **Performance** section name in the left panel, the **Conversion Goals** window opens. This view provides detailed information about all goals configured for your journey, including their performance metrics and any defined conditions. ![Conversion Goals performance window showing conversion rates and goal details](https://docs.pushwoosh.com/customer-journey-statistics-20.webp) In this window, you can: * Review conversion data for each goal. * View goal conditions. * See the conversion period, which shows how long conversions are tracked after a user exits the goal point (for example, *30 days after exit*). * To save goal statistics, click **Download CSV** beside any goal to export its data as a CSV file. Tip * [Define clear and measurable goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) in your campaign to track important outcomes such as logins, signups, or payments. * Use the CSV download feature to save performance data, compare campaign results, or share insights with your team. ## Audience The **Audience** dashboard provides detailed information on how users interact with your campaign. It tracks user activity across the journey, including entries, exits, errors, and individual user statuses. ![Audience dashboard displaying user statistics including entrances, users in journey, exits, internal errors, and sending errors](https://docs.pushwoosh.com/customer-journey-statistics-17.webp) The dashboard includes the following key metrics: | | | | ------------------- | --------------------------------------------------------------------------------------------------------------------- | | **Entrances** | Total number of users who entered the campaign during the selected time period, including repeat entries. | | **In journey now** | Number of users currently active in the journey. | | **Exits** | Number of users who have completed or exited the journey. | | **Internal errors** | User drop-offs from the journey caused by technical issues. | | **Sending errors** | Delivery failures caused by technical problems such as an uninstalled app, invalid email/phone, or a blocked gateway. | If a branch has no outgoing connection and ends without an Exit element, users who reach its end count toward **Exits**, not toward the error rows. The user list below displays individual journey records, helping you monitor specific cases and troubleshoot issues: | | | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **User ID / HWID** | A unique identifier for each user. Use the search bar to find a specific user by HWID or User ID. | | **Status** | Indicates the user’s current state in the journey (e.g., *In Progress*, *Exited*, *Dropped off*). | | **Last step** | Shows the last completed step or the point where the user exited the journey. If a user exited due to a [cancel event](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#remove-users-from-a-journey-without-counting-a-conversion), the column displays “Exit by Cancel event: [event name]” (e.g., “Exit by Cancel event: Cart Cleaned”). | Click **Export CSV** to download the full list of user records and their statuses for further analysis. ## Communications The **Communications** section provides detailed insights into how each messaging channel performs in your campaign. It helps you evaluate message delivery, engagement, and potential delivery issues so that you can optimize your communication strategy. To open this view, click the **Communications** section name in the left panel. This opens the **Communications statistics** window where you can see how your messages are performing. ![Communications statistics window displaying message performance metrics by channel](https://docs.pushwoosh.com/customer-journey-statistics-21.webp) If your campaign includes multiple channels, separate tabs will be shown at the top of the window. Select a tab to switch between channels and review the metrics for each one. ![Communications tabs showing Push, Email, SMS, and WhatsApp communication options](https://docs.pushwoosh.com/customer-journey-statistics-22.webp) Tip To export communication statistics, click **Export statistics** in the header of the Communications statistics window. ![Export statistics button in Communications statistics window header](https://docs.pushwoosh.com/customer-journey-statistics-23.webp) ### Metrics by channel The metrics displayed in the Communications statistics window depend on the channel. Each channel tracks the statistics that are most relevant for that message type. #### Push, in-app, and email * Push notifications | Metric | What it measures | | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Sent** | Total number of push notifications sent, including sends to opted-out users and failed sending attempts. | | **Recipients** | Push notifications Pushwoosh successfully sent to devices with push notifications enabled: Sent minus failed sends (for example, devices without a valid push token) and sends to users who have opted out. Each user may have multiple devices, and each device counts as a separate recipient. | | **Opened** | Number of unique message opens, shown as a percentage of Recipients. `Open rate = opens ÷ Recipients × 100%` | | **Delivery issues** | Number of messages that could not be delivered, shown as a percentage of users who entered any push step in the journey. | * In-apps | Metric | What it measures | | ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | | **Impressions** | Number of times in-app messages were displayed to users. | | **Interactions** | Number of user interactions with in-app messages, shown as a percentage of Impressions. `Interaction rate = Interactions ÷ Impressions × 100%` | | **Delivery issues** | Number of messages that could not be delivered, shown as a percentage of users who entered any in-app step in the journey. | * Emails | Metric | What it measures | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Delivered** | Total number of successful email deliveries, including repeated deliveries to the same recipient. `Delivery rate = Delivered ÷ recipients × 100%` | | **Unique delivered** | Number of unique users who had at least one message successfully delivered, shown as a count and as a percentage of recipients. `Unique delivery rate = unique deliveries ÷ recipients × 100%` | | **Sent** | Total number of emails sent, including failed sending attempts. | | **Opened total** | Total number of email opens, including repeat opens by the same recipient, shown as a percentage of recipients. `Total open rate = total opens ÷ recipients × 100%` | | **Opened unique** | Number of unique recipients who opened the message at least once, shown as a percentage of recipients. `Unique open rate = unique opens ÷ recipients × 100%` | | **Clicked** | Number of recipients who clicked any link in the email, shown as a percentage of recipients. `Click rate = link clicks ÷ recipients × 100%` | | **Unsubscribed** | Number of recipients who unsubscribed, shown as a percentage of recipients. `Unsubscribe rate = unsubscribes ÷ recipients × 100%` | | **Hard bounce** | Number of emails not delivered due to permanent issues (e.g., invalid address), shown as a percentage of recipients. `Hard bounce rate = hard bounces ÷ recipients × 100%` | | **Soft bounce** | Number of emails not delivered due to temporary issues (e.g., full inbox or server error), shown as a percentage of recipients. `Soft bounce rate = soft bounces ÷ recipients × 100%` | | **Complaints** | Number of recipients who marked the email as spam, shown as a percentage of recipients. `Complaint rate = spam reports ÷ recipients × 100%` | | **Delivery issues** | Number of messages that could not be delivered, shown as a percentage of users who entered any email step in the journey. | #### SMS and messengers * WhatsApp | Metric | What it measures | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Sent** | Total number of WhatsApp messages sent, including failed sending attempts. | | **Delivered** | Number of messages successfully delivered, shown as a percentage of recipients — WhatsApp messages successfully sent out (Sent minus failed sends). `Delivery rate = Delivered ÷ recipients × 100%` | | **Read** | Number of messages read by users, shown as a percentage of recipients. `Read rate = Read ÷ recipients × 100%` | | **Delivery issues** | Number of messages that could not be delivered, shown as a percentage of users who entered any WhatsApp step in the journey. | * SMS and other messengers SMS, Kakao, Telegram, Viber, and Facebook Messenger show the same metrics. Each channel has its own tab in the Communications statistics window. | Metric | What it measures | | ------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | **Sent** | Total number of messages sent in the channel, including failed sending attempts. | | **Delivered** | Number of messages successfully delivered, shown as a percentage of Sent. `Delivery rate = Delivered ÷ Sent × 100%` | | **Delivery issues** | Number of messages that could not be delivered, shown as a percentage of users who entered any step of that channel in the journey. | Below the channel metrics, each channel’s statistics include a **Frequency capping** row. It shows how [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) affected this journey’s messages over the selected period: * **Suppressions**: the number of messages that were not sent because the recipient had already reached their frequency capping limit. * **Affected users**: the number of unique users who had at least one message suppressed by frequency capping. The **Performance** chart visualizes message activity over time, helping you identify engagement trends and delivery patterns. ![Performance chart showing message activity trends and engagement patterns over time](https://docs.pushwoosh.com/customer-journey-statistics-24.webp) ##### Message-level statistics The **Messages** section below the Performance chart lists all communication steps for the selected communication channel. Use this table to compare how individual messages within this channel perform and to understand how each message contributes to your campaign goals. The Kakao, Telegram, Viber, and Facebook Messenger tabs don’t have this table. At the top of the table, select a conversion goal to see how each message influences that specific goal. To download all message-level data, click **Export channel statistics** to export a CSV file for analysis or reporting. Tip Click any message in the table to open a detailed view with full statistics for that message. ![Message-level statistics table showing sent, opened, clicked, and conversion metrics](https://docs.pushwoosh.com/customer-journey-statistics-25.webp) For each message, the table displays: * **Sent**: The total number of messages sent. * **Opened**: The percentage and number of users who opened the message. * **Clicked**: The percentage and number of users who clicked on links. * **Goal conversion**: The percentage and number of users who completed the selected conversion goal after interacting with the message. To export data for an individual message, click the three dots menu next to the message name and choose one of the following options: * **Export point statistics**: Downloads detailed performance metrics for that message. * **Export point users**: Downloads a list of users who interacted with that message. **Export point users** requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. Tip To view link-level statistics for an email message, click **Click to expand link performance** under any message. This will expand the message row and show detailed data for every link and link variant included in the email. Link-level statistics are available only for email messages. ##### Delivery issues The **Delivery issues** section provides a detailed breakdown of failed or skipped message deliveries. It helps you identify and address common issues that may prevent messages from reaching users. Each issue includes a count, rate, and reason, giving you transparency into delivery reliability. ![Delivery issues breakdown showing failed message counts, rates, and error reasons](https://docs.pushwoosh.com/customer-journey-statistics-26.webp) ### Events brought by journey messages The **Events** block under the channel tabs shows which custom events users sent after clicking this journey’s messages, and which steps brought them. It fills in only if your app or website sends events to Pushwoosh. Each event goes to the message the user last clicked within the 72 hours before the event. A click depends on the channel: * **Push and web push:** a tap that opens the notification * **App Inbox:** opening the message * **Email:** a click on a link, except the unsubscribe link. Opening the email doesn’t count. * **WhatsApp:** reading the message SMS has no clicks, so SMS steps bring no events. Clicks on all messages of the same app count, including messages outside this journey, so an event goes to whichever message the user clicked last. One event goes to one message only, and clicks older than 72 hours don’t count. The block shows these columns: | Column | What it shows | | :------------------ | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Event name** | The event name. Events set as [conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) are pinned on top with a **Goal** badge, even when no user sent them yet. | | **Events** | How many events went to the journey’s messages in the selected period. | | **Users** | How many distinct users sent the event, matched by [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/). | | **% of recipients** | **Users** as a share of the journey’s recipients across all channels. | ![Events block in Communications statistics with a Purchase goal row expanded into the two push steps that brought it](https://docs.pushwoosh.com/customer-journey-statistics-28.webp) The line above the table shows the period the block covers. It follows the **Period** filter of the statistics page and filters the events, not the clicks. A click before the start of the period still counts for an event inside the period. To work with the block: * **See which steps brought an event:** Click its row. The row expands into the journey steps whose message the user clicked last, with **Events** and **Users** for each step. A step removed from the journey shows as **Deleted step**. * **Find an event:** Type its name in **Search event**. * **Download the table as CSV:** Click **Export events**. The file includes each event’s breakdown by step. Note Goal rows here count events by last click. The goal numbers in [Performance](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#performance) use the journey’s own [conversion goal attribution](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), so the two can differ. ## Campaign health The **Campaign health** dashboard provides a high-level summary of your campaign’s performance, helping you quickly evaluate user engagement, message delivery, and goal progression. The **Campaign health** card in the left panel shows **Entrances**, **Goals achieved**, and **Drop-offs**. If the journey has no [conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), or a goal is missing its event or app, **Goals achieved** shows **Not set** instead of a number. ![Campaign health dashboard showing high-level summary metrics including entrances, messages sent, messages opened, and drop-offs](https://docs.pushwoosh.com/customer-journey-statistics-16.webp) It includes the following key metrics: | | | | ------------------- | --------------------------------------------------------------------------------------------------------- | | **Entrances** | Total number of users who entered the campaign during the selected time period, including repeat entries. | | **Messages sent** | Total number of messages sent within the journey. | | **Messages opened** | The number and percentage of messages that were opened by users. | | **Drop-offs** | The number of users removed from the campaign due to internal issues. | The charts below help visualize key trends and behaviors throughout the campaign duration: * **Entrances over time** Displays the number of users entering the campaign each day. * **Messages sent over time** Shows daily message delivery volume across different channels. * **Drop-offs over time** Tracks when and why users exited the campaign, categorized by: * **Internal errors**: Issues related to system logic or conditions. * **Sending errors**: Failures related to message delivery. * **Goals achieved over time** Displays how many users completed campaign goals during the selected time range. If the journey has one or more [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) points, a **Goal uplift vs holdout** report appears below the charts, one for each point. It compares goal conversion between users who continued through the journey and users held out at that point. ## Export campaign statistics Pushwoosh allows you to export comprehensive campaign statistics for a selected date range in CSV format. This provides detailed insights into your campaign’s performance, helping you analyze key metrics and optimize future campaigns. Depending on your needs, you can export data for a single journey, multiple journeys with different statuses or categories, or even all journeys at once. To export campaign statistics: 1. Navigate to the **Campaigns list**. 2. Locate the desired campaign or use filters to narrow down your journeys based on status, category, or other criteria. The exported CSV file will reflect these filters. Then, click **Export statistics**. To export statistics for all your journeys, simply click **Export statistics.** ![Export statistics button in Campaigns list for exporting journey performance data](https://docs.pushwoosh.com/customer-journey-statistics-10.webp) 3. Select the date range for which you want to export the data and click **Export**. Note Pushwoosh stores journey data indefinitely, allowing you to access and export data for journeys that occurred even years ago. However, to keep file sizes manageable, you can only export a maximum of 31 days of data per CSV file. ![Date range selection dialog for exporting campaign statistics](https://docs.pushwoosh.com/customer-journey-statistics-11.webp) 4. Once the file is ready, click **Download CSV**. Information included in the CSV export The CSV will contain the following information: * **Title**: Name of the campaign or journey. * **Status**: Current status of the journey (e.g., finished, active). * **Created**: The date and time when the journey was created. * **Category**: The category of the campaign (e.g., Black Friday promo, Coffee promo). * **Type**: The type of journey (e.g., AudienceBased, TriggerBased). * **UUID**: A unique identifier for the journey. * **Total Reach**: Total number of users targeted by the journey. * **Unique Users**: Number of unique users who received messages in the journey. * **Users Inside**: Number of users still active within the journey. * **Drop-off**: Number of users who exited or dropped off from the journey. * **Total Sends**: Total number of messages sent across all channels (push, email, SMS, etc.). * **Avg CTR**: Average click-through rate across all message types. * **Goals**: Number of users who reached the defined goals in the campaign. * **Push Sends**: Number of push notifications sent in the journey. * **Push CTR**: Click-through rate for push notifications. * **Email Sends**: Number of email messages sent in the journey. * **Email CTR**: Click-through rate for email messages. * **Email Clicks**: Total number of clicks on email links. * **Email Unsubscribes**: Number of users who unsubscribed from your email list. * **SMS Sends**: Number of SMS messages sent in the journey. * **SMS Deliveries**: Number of SMS messages successfully delivered. * **WhatsApp Sends**: Number of WhatsApp messages sent in the journey. * **WhatsApp Opens**: Number of WhatsApp messages opened by users. * **In-App Shows**: Number of in-app messages displayed to users. * **In-App Interaction**: Number of users who interacted with in-app messages (e.g., clicked a button). * **In-App Skips**: Number of users who skipped or dismissed the in-app message without interacting. --- # Message history **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/ Review push notifications and emails (up to one year), search and filter, edit or cancel scheduled messages, and open detailed statistics. --- Pushwoosh provides a convenient way to monitor your past push notifications and emails for one year. From Message History you can search and filter the list, edit or cancel scheduled one-time messages, and open detailed statistics for any message. ## Message list To access Message History, go to **Campaigns → One-time messages.** Use the search bar to filter messages by campaign name, text, notification ID or code. You can enter the full value or a part of it. ### Filter by message type Above the list, use the **message type** dropdown to quickly narrow the messages shown: * **All:** shows both marketing and transactional messages (default). * **Marketing:** shows only marketing messages (campaigns and promos). * **Transactional:** shows only transactional messages (operational or service messages). Note With the **Transactional** filter, messages are shown only for the last month and do not include Live Activities. To view messages over a longer time range, adjust the date range. To include Live Activities, select **Live Activity API** in [Advanced Filters](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#advanced-filters) or [search by a Live Activity ID](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#find-a-live-activity-by-its-id). [Learn more about marketing and transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/) ### Filter by date range Use the date range filter in the table header to show only messages from a specific period. 1. Open the date type dropdown and choose which date the range applies to: * **Send date:** filter by send date. * **Last modified:** filter by the date the message was last edited. 2. Use the calendar to select the start and end dates. By default, Message History shows messages sent in the last 30 days. A range must fall within the last 12 months and can be up to 3 months long. If your browser’s timezone doesn’t match your [account timezone](https://docs.pushwoosh.com/product/account-management-and-security/account-settings/#default-timezone), Message History shows a banner offering to switch your account to the browser’s timezone. Closing the banner hides it for that browser going forward. Below the banner, a label names the account timezone the list’s dates use, for example **Dates in EDT (UTC-04:00)**. For a zone without a common letter abbreviation, such as `Asia/Karachi`, the label shows the UTC offset instead, for example **Dates in UTC+05:00**. ### Sort the list In the toolbar above the table, to the right of the date range filter, click the sort dropdown to order the list. The button label shows the active sort field (for example, **Last modified**). * **Last modified:** sort by last edit date (default). * **Created:** sort by creation date. * **Send date:** sort by send date. * **Recipients:** sort by recipient count. * **Opened:** sort by unique opens. * **Open rate:** sort by open rate. * **Errors:** sort by error count. ### Advanced Filters Use **Advanced Filters** to specify platforms, message source (API, Control Panel, AutoPush, Geozone, SMS API, WhatsApp API, Test email, and others), segments, and campaigns. ![Advanced Filters dialog with options to filter by platform, message source, segments, and campaigns](https://docs.pushwoosh.com/statistics-and-analytics-message-history-2.webp) ### Find a Live Activity by its ID To see all messages of one [Live Activity](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) (a lock-screen card that updates in real time) started through the [API](https://docs.pushwoosh.com/developer/api-reference/ios-live-activities-api/), enter its full `live_activity_id` in the search bar. The ID is case-sensitive and can’t contain spaces. The list shows that Live Activity’s messages within the selected [date range](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#filter-by-date-range), regardless of the [message type](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#filter-by-message-type) filter. If you select sources in [Advanced Filters](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#advanced-filters), include **Live Activity API**. Without it, or when no Live Activity matches the ID, the search works as a regular search. ## Message statuses The **Status** column shows the current state of each message: | Status | Description | | -------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CREATING** | Message was just created and is being prepared. | | **SCHEDULED** | Send is scheduled for the future: the send date and time haven’t arrived yet. This includes delayed messages and timezone-based or Best time sends that haven’t started. You can edit or cancel the message. | | **PENDING** | The message is queued to send now (the send time has already arrived), or sending has just started. This is a short, transient state that is rarely visible. | | **WAITING** | The message is being delivered in subscribers’ local timezones — Pushwoosh sends it hour by hour as each timezone reaches the scheduled time, and the status stays **Waiting** until all timezones are covered. | | **PROCESSING** | Message is being sent. | | **DONE** | Sending completed successfully. | | **FAIL** | Sending failed. [Message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#view-detailed-statistics) shows the reason. See [Why a message failed](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed). | | **CANCELED** | Scheduled message was canceled before send (e.g. via **Delete message**). | ### Why a message failed When a message has the **FAIL** status, its Message stats **Overview** tab shows dashes instead of numbers and a notice with the reason the send stopped. The notice text depends on the reason: | Notice in Message stats | What happened | What to do | | -------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | This message was not sent: its audience filter could not be built. Check the segment conditions | Pushwoosh could not build the audience from the segment. | Open the segment and check its conditions, then send the message again. | | This message was not sent: its segment no longer exists | The segment the message targets was deleted. | Choose an existing segment and send the message again. | | This message was not sent: the application of its segment no longer exists | The application the segment belongs to was deleted. | Choose a segment of an existing application and send the message again. | | This message was not sent: its email category was deleted | The email category set on the message was deleted. | Choose an existing email category and send the email again. | | This message was not sent: its send date is missing or invalid. Schedule it again | The message had no valid send date when Pushwoosh started processing it. With the **Best time** sending option, the same notice appears when the message has no valid day to send on. | Create the message again and set its send date. If the notice appears again, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the [message code](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#copy-the-message-code). | | This message was not sent because of an internal error. Contact support and share the message code | The send stopped on the Pushwoosh side, or Message stats has no specific text for the reason. Messages that failed with no recorded reason also show it. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the [message code](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#copy-the-message-code). | If sending stopped after part of the audience was already reached, Message stats shows the numbers collected so far with the notice **Sending stopped because of an error, so this message’s numbers are incomplete**. A [test email](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#send-a-test-email) shows a grey **Test** badge next to its status, so test sends are never mistaken for a live campaign. ## Edit or cancel scheduled one-time messages Only messages with status **Scheduled** (the send time hasn’t been reached yet) can be edited or canceled. You can do this from Message History or from **Campaigns** → **One-time messages**. Open the actions menu (⋮) for the message: * **Delete message:** cancels the scheduled delivery. Status becomes **Canceled**. * **Edit message:** opens the campaign form so you can change the message. At the top of the form, below the step indicator, **Last edit** shows the date, time, and user who last changed the message. * **Rename:** opens a small dialog to set or change the campaign name, without reopening the full edit form. Same 255-character limit as the Campaign name field you set at send time. Clear the field and save to remove the name. The row reverts to its previous default title. * **Copy message code:** copies the message code to your clipboard. Available for any message in the list. ![One-time messages actions menu open, showing Edit message, Rename, and Delete message](https://docs.pushwoosh.com/statistics-and-analytics-message-history-7.webp) Clicking **Rename** opens a dialog to set or change the campaign name: ![Edit campaign name dialog with a Campaign name field prefilled with the message's current name](https://docs.pushwoosh.com/statistics-and-analytics-message-history-8.webp) Editing limits * When editing, you can update **content** and **audience** only. The scheduled send time is fixed and cannot be changed. To change the send time, cancel the message (**Delete message**) and create a new one. * On the **Audience** step you can change the segment, create a new one, or change the device list or list of addresses. The audience size is recalculated after you make a change. * If the message enters **Processing** (message is being sent) while you are editing, saving is disabled. Use **Go to Message History** or **Close** to leave the form. ## View detailed statistics Click the message name in the **Content** column to open Message stats for that send (**Overview**, then **Clicks** for an email, then **Timeline** and **Errors**). A message [deleted through the API](https://docs.pushwoosh.com/developer/api-reference/messages-api/#deletemessage) after it was already sent is deleted for good, with no way back. It may still appear in the list under its last status, but you can no longer search for it by message code or ID or open its Message stats. If a message has a **Campaign name** (set in the Confirm sending dialog when it was sent or scheduled), the **Content** column uses it as the row’s bold title, with the message’s own title, subject line, template, or preset name shown on the grey line below. Without a campaign name, the row falls back to its previous default: for an SMS sent from an [SMS preset](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/), the **Content** column names the row after the preset (instead of **Untitled**) and shows the preset’s text below the row name. ![Message History Content column with a campaign name as the bold row title and the notification title demoted to the grey secondary line, next to rows without a campaign name](https://docs.pushwoosh.com/statistics-and-analytics-message-history-5.webp) Opening a row’s Message stats shows **Message code** and **Export stats** buttons at the top of the page, regardless of whether the row has a campaign name. ![Message stats header opened from Message History with Message code and Export stats buttons at the top](https://docs.pushwoosh.com/statistics-and-analytics-message-history-4.webp) For a message that’s still **Scheduled**, the header also shows an actions menu (⋮) next to **Message code** and **Export stats**, with a single **Rename** option. It opens the same dialog as [Rename](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#edit-or-cancel-scheduled-one-time-messages) in the list’s actions menu. Once a campaign name is set, the page title adds it after a colon, for example **Message stats: Flash_Sale_Push** instead of plain **Message stats**. ![Message stats header for a Scheduled message reading Message stats: Flash_Sale_Push, with the actions menu open showing Rename](https://docs.pushwoosh.com/statistics-and-analytics-message-history-9.webp) Tip To follow up with recipients who didn’t open an email, turn on [**Resend to non-openers**](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#resend-to-non-openers) when you compose the email, before you send or schedule it. You can’t enable it afterward from Message stats. ## Export message history To export message history data, click the **Export** button in the top-right corner of the Message History page. In the export dialog, select which fields to include. Once the export is ready, you will receive a notification with a **Download CSV** link. ![Export Message History to CSV dialog with the Campaign name checkbox in the list of message properties](https://docs.pushwoosh.com/statistics-and-analytics-message-history-6.webp) The export includes only the messages matching your current [message type filter](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#filter-by-message-type) and other applied filters. For example, exporting with the **Transactional** filter active produces a CSV of transactional messages only. Exporting while the search bar holds a [Live Activity ID](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#find-a-live-activity-by-its-id) produces a CSV of that Live Activity’s messages. Note You can export up to 31 days of data per CSV file. If your export exceeds 1,000,000 rows, the file is still delivered, but includes only the first 1,000,000 rows. ### Message details | Field | Description | | ------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | | **ID** | Unique numeric identifier of the message. For transactional messages this field is `0`; use **Message code** to identify the message instead. | | **Message code** | [Message code](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#message-code) used for tracking or API interactions. | | **Campaign name** | The campaign name set for the message, if any. Empty when none was set. | | **Send date** | Date and time when the message was sent. | | **Content** | Notification title and body text. | | **Applications** | Application or application group the message was sent from. | | **Status** | Final delivery status. | | **Platforms** | List of platforms the message was targeted to (e.g. iOS, Android). | | **Source** | How the message was created: API, Control Panel, Customer Journey, Test email, etc. | | **Filter code** | Code of the audience filter used for targeting. | | **Filter conditions** | Conditions expression of the audience filter. | | **Filter name** | Name of the audience filter. | | **Subscription segments** | Subscription-based segments included in the send. | ### Delivery and engagement metrics | Field | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------- | | **Total sent** | Total number of send attempts dispatched to the provider. | | **Total recipients** | Number of sent messages that could be displayed on the recipient’s device (total sent minus sends to devices that could not receive the message). | | **Total delivered unique** | Number of unique delivery confirmations — each device counted once even if it received retries. | | **Total delivered** | Total delivery confirmations including retries and repeated deliveries to the same device. | | **Total opened unique** | Number of unique users who opened the message. *(previously `Total opened`)* | | **Total opened** | Total open events, including repeated opens by the same user. *(previously `Total all opened`)* | | **Total errors** | Number of send-time errors returned by the provider (e.g. invalid token, message rejected). | | **Total clicked unique** | Number of unique users who clicked at least one link in the message. Requires link tracking to be enabled. *(previously `Total clicks`)* | | **Total clicked** | Total link click events across all users, counting repeated clicks. Requires link tracking to be enabled. *(previously `Total all clicks`)* | | **Total unsubscribed** | Number of unsubscribe actions triggered in response to this message. | Learn more about: [Push statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/#detailed-push-notification-statistics/) [Email statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#detailed-email-statistics/) [SMS and messenger statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#detailed-sms-and-messenger-statistics/) --- # Understanding messaging statistics in Pushwoosh **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/ Estimate the efficiency of your campaigns with detailed statistics. Track individual message performance and overall channel trends. --- Pushwoosh provides an in-depth analysis of your campaigns, allowing you to gain insights into individual message performance and overall channel effectiveness. This means you can: * Track individual messages and see how each push notification, in-app message, and email within a campaign performs. * Understand overall trends in channel performance and gain insights into how different channels resonate with your audience. By comparing the effectiveness of push notifications, in-app messages, and emails, you can identify the best channels for your app and campaign goals. Imagine you’re running a multichannel campaign to guide new users seamlessly toward premium app features. For this, in your customer journey, you include the following messages: * An email that welcomes users and introduces your app’s key features and subscription benefits. * A push notification to remind them of the premium upgrade benefits and nudge them to complete the subscription process for exclusive content. * An in-app message to offer a discount or bonus to users who engaged with your push notification but haven’t subscribed yet. Pushwoosh allows you to track the performance of each of these messages directly from the journey canvas. And if you aim to assess the overall performance trends of each messaging channel, Pushwoosh offers a simple solution: [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/). They provide an overview of each channel’s performance and allow you to see which messaging formats and campaigns resonate with your audience more. Using data on specific messages’ performance and overall trends for each channel, you can optimize your omnichannel engagement strategy and boost campaigns’ effectiveness. Learn more about: [Push notification statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/) [App Inbox statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/) [In-app statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) [Email statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) [SMS and messenger statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/) [Web campaign statistics ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/) [How to read the Errors tab ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) [Messaging statistics FAQ ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/) --- # App Inbox statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/ Read the App Inbox delivery funnel, from audience to clicks, and find delivery errors by platform for a journey App Inbox element. --- App Inbox statistics show how many people actually got the message, how many opened it, and how many tapped it, plus where you lose the rest along the way. Tip When delivery looks low, check the **Errors** tab for the reason. ## Open App Inbox statistics 1. Double-click the **App Inbox** element on a journey canvas. 2. Check the panel’s metrics for this element. See [Metrics on the canvas](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/#metrics-on-the-canvas) below. 3. Click **Detail statistics** to open the full **Message stats** page, with **Overview**, **Timeline**, and **Errors** tabs. ### Metrics on the canvas Double-click the **App Inbox** element on a journey canvas to open this panel. It counts people, not messages, giving you a quick read on delivery and engagement before you open [Detailed statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/app-inbox-statistics/#detailed-statistics) below. ![App Inbox canvas panel showing Sent, Reached, Read, and Clicked cards, plus Entries, Dismissed, Expired, Skipped, and Frequency capping skipped counts](https://docs.pushwoosh.com/statistics-app-inbox-statistics-4.webp) | Metric | What it means | | :---------------------------- | :------------------------------------------------------------------------------------------------------------------------ | | **Entries** | Everyone this element tried to reach. | | **Sent** | People who got the message in their inbox. | | **Reached** | People who have the message in their inbox. It appears when they open the app, even if they haven’t looked at it yet. | | **Read** | People who opened and looked at the message. | | **Clicked** | People who tapped the message. | | **Dismissed** | People who deleted the message, whether or not they’d read it first. | | **Expired** | People whose message disappeared before they read it. A high number means people need more time. Try a longer expiration. | | **Skipped (no app)** | People with no mobile or web app set up to show an inbox, so the message had nowhere to go. | | **Frequency capping skipped** | People who’d already hit their App Inbox limit, so this message wasn’t delivered. | ## Detailed statistics Use **Message stats** to check how this App Inbox element performed, from audience to clicks. ### Overview tab Use the **Overview** tab to check delivery and engagement for this element. #### Campaign performance These cards summarize how this App Inbox element performed. ![Campaign performance cards for an App Inbox element: Audience, Sent, Reached, Read, and Clicked](https://docs.pushwoosh.com/statistics-app-inbox-statistics-1.webp) | Card | What it shows | Formula | | :----------- | :----------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------- | | **Audience** | Total audience users for this element, and how many stayed eligible after exclusions (frequency capping or a control group). | Eligible users + excluded users | | **Sent** | Share **of total audience** who got the message in their inbox. | Placed into inboxes ÷ total audience | | **Reached** | Share **of sent** users whose app picked up the message at least once. It’s on their device, but they may not have looked at it yet. | Reached ÷ sent | | **Read** | Share **of reached** users who opened the message, whether or not they later deleted it. | Read ÷ reached | | **Clicked** | Share **of read** users who tapped the message. Repeat taps count once. | Clicked ÷ read | #### Campaigns distribution **Campaigns distribution** shows how users move through the funnel. Switch between **Funnel view** and **Table view** with the dropdown in the block header. ![Campaigns distribution funnel for an App Inbox element, from Audience through Sent, Reached, and Read to Clicked](https://docs.pushwoosh.com/statistics-app-inbox-statistics-2.webp) Each row shows how many people reached that stage, and why some didn’t move to the next one: | Stage | What it means | | :----------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Audience** | Everyone this element targeted. Some are excluded before anything sends: they already hit their [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#app-inbox) limit, or they’re in a global control group. | | **Sent** | Almost everyone eligible gets the message in their inbox. A few don’t, usually a bad device or token, or a delivery error. Check the **Errors** tab for the reason. | | **Reached** | Most sent messages get picked up the moment the user opens the app. The rest are just waiting: their app hasn’t fetched the message yet, which isn’t a delivery failure. | | **Read** | Most reached messages get opened. Some people delete a message without reading it, and some read it and then delete it. Either way, once it’s deleted it won’t count as unread. | | **Clicked** | Some people who read the message go on to tap it. Repeat taps count once, the same way **Read** counts repeat opens once. | How to read the funnel * Follow the path down: **Audience → Sent → Reached → Read → Clicked**. * The percentage on a step is the share that reached it from the step above. The canvas panel above computes its own Read and Clicked percentages differently, against **Sent** rather than the step right before them, so the two can show different numbers for the same send. * The **Sent** card above and this funnel’s **Sent** row can disagree: the card counts messages accepted by the gateway, this row counts everyone still eligible before that gateway accepted or rejected them. * The rows under **Read** don’t add up to its total: people who were reached but haven’t opened or deleted the message yet stay off this list by design, they’re not a separate outcome. * A low **Reached** number often just means people haven’t opened the app yet. It climbs over time, it isn’t a delivery failure. * A low **Read** number can mean the expiration is too short. Try a longer window in [Set expiration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/#set-expiration). * A low **Clicked** number next to a healthy **Read** number points to the content, not delivery. Test a different message or a clearer call to action. * If **Sent** itself is low, check the **Errors** tab first. ### Timeline tab Use the **Timeline** tab to check the custom events that followed this message. App Inbox has no delivery chart, so the tab shows **No delivery chart for App Inbox** in its place and the **Events** table under it. Set the period at the top of the tab to choose which events the table counts. ![App Inbox Timeline tab with the No delivery chart for App Inbox notice and the Events table below it](https://docs.pushwoosh.com/statistics-app-inbox-statistics-5.webp) #### Custom events after opening this message The **Events** table shows the custom events your app sent after users opened this message in the inbox. It fills in only if your app sends events to Pushwoosh. Each event goes to the message the user last clicked within the 72 hours before the event: * **What counts as a click:** Opening the message. * **Clicks on other messages:** Clicks on all messages of the same app count, so an event that follows a click on a later message goes to that later message, not to this one. * **Limits:** One event goes to one message only. | Column | What it shows | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | **Event** | The event name. | | **Hits** | How many events went to this message. Every event the user sends after opening it counts, until the user clicks another message or 72 hours pass. | | **Unique** | How many distinct users sent the event at least once. | Events are matched to the message by [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/), not by device. The period filters the events, not the opens. An open before the start of the period still counts for an event inside the period. Each row has an export icon that downloads the devices behind that row as CSV, counted by the same rule. Tip If the table is empty, no custom events followed opens of this message in the selected period, or your app doesn’t send events to Pushwoosh. You cannot turn event tracking on from Message stats. Ask your development team whether the app sends [custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) to Pushwoosh. ### Errors tab Use the **Errors** tab to see why sends were rejected. See [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) for what the tab shows, and [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) for what each error code means. --- # Email statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/ Explore email analytics to track delivery, opens, clicks, and engagement for each message and across all your email campaigns. --- In Pushwoosh, email analytics provide valuable insights into the performance of your messages. You can evaluate individual email performance and track overall trends across your email campaigns. Note Pushwoosh uses service email headers (sometimes called X-headers) to track email delivery, complaints, bounces, and analytics for troubleshooting purposes. These headers cannot be stripped out of your email messages on demand. However, some email clients may strip out X-headers of your message. ## Track individual email message performance You can monitor the performance of each email message within a customer journey directly on the **Journey canvas**. To view statistics for a message, open [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) and turn on **Show metrics**, or hover over the message element to see the stats. [](https://docs.pushwoosh.com/emails-email-statistics-1.webm "Enabling the Show metrics toggle in Canvas settings") Below the email message element, you will see such metrics as: | | | | ----------------- | ------------------------------------------------------------------------------------- | | **Goals Reached** | Indicates the number of users who reached the Conversion Goal at this specific point. | | **Opened** | Number and percentage of unique users who opened the email (each user counted once). | | **CTR** | Represents the percentage of users who clicked on the message. | | **Drop-offs** | Represents the number of messages that couldn’t be sent. | Note By default, statistics are displayed for the period since the journey launch. You can choose a different period from the panel on the left side and then click **Apply time range**. For a detailed view of individual message effectiveness, double-click on a message element on the canvas. A statistics window will pop up, displaying key metrics, such as: * Number of sent messages * Number of opened messages * Number of clicks * Number of recipients who have unsubscribed ![detailed statistics on the journey canvas](https://docs.pushwoosh.com/emails-email-statistics-2.webp) To see the reasons for drop-offs and the number of drop-offs for each cause, expand the **Drop-offs** block. ![you can see the reasons for drop-offs and the number of drop-offs](https://docs.pushwoosh.com/emails-email-statistics-3.webp) ## Detailed email statistics To view detailed statistics for an email message, click **Detailed Statistics** in the journey email element. Alternatively, open **Campaigns → [One-time messages](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/)**, find the message, and click it. Use **Message stats** to check how a specific email performed, from audience targeting to opens and clicks. The page tabs are **Overview**, **Clicks**, **Timeline**, and **Errors**. If the same send also went to other channels, channel tabs appear above them so you can pick which channel to view. A single-channel message shows no channel tabs. ### Overview tab Use the **Overview** tab to check reach, delivery, opens, and (for broadcast sends) clicks and unsubscribes for this email. If the message has the **FAIL** status, the **Overview** tab shows dashes instead of numbers and a notice with the reason it was not sent. See [Why a message failed](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed). #### Campaign performance Start here to see how far this email got: who you reached, how many opens and clicks you got, and whether people unsubscribed or marked spam. Hover the ⓘ on any card for the exact formula. ![Campaign performance cards and Campaigns distribution funnel for an email Message stats Overview](https://docs.pushwoosh.com/emails-email-statistics-27.webp) | Card | What it shows | | :--------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Audience** | How many people you targeted for this send, and how many stayed eligible after exclusions. | | **Sent** | Of everyone you targeted, what share the email provider accepted for sending. Accepted means the provider took the message into work, not that it reached the inbox. | | **Delivered** | Of those accepted for sending, what share did not hard- or soft-bounce. Different from the **Deliveries** funnel step below, which tracks delivery confirmations. | | **Opened** | Of delivered recipients, what share opened the email at least once. | | **Clicked** | Of people who opened (including Apple Mail Privacy Protection machine-only opens), what share clicked a link. Someone who later unsubscribed or marked spam still counts here. | | **Unsubscribed** | Of delivered recipients, what share unsubscribed or marked the email as spam. Spam complaints count here too, because the mailbox provider unsubscribes them. This can be higher than the funnel row for “unsubscribed without clicking”. | #### Campaigns distribution **Campaigns distribution** breaks this email into steps: * **Audience:** who you targeted * **Sent:** who was accepted for sending * **Deliveries:** who got a delivery confirmation * **Opened:** who opened * **Interactions:** who clicked, unsubscribed, or complained (broadcast sends only) ![Campaigns distribution funnel for an email with Audience, Sent, Deliveries, and Opened steps](https://docs.pushwoosh.com/emails-email-statistics-28.webp) Transactional emails have no **Interactions** step, and their **Clicked** and **Unsubscribed** cards above read zero as well. You can view the same breakdown in two ways: as a funnel (default) or as a table. Use the view dropdown in the block header to switch between **Funnel view** and **Table view**. Click **Refresh** to reload the numbers. Note The funnel and the cards above always show the full history of this email. The date range on **Timeline** does not change these numbers. ##### How to read the funnel * Follow the path down: **Audience → Sent → Deliveries → Opened → Interactions** (Interactions only on broadcast sends). * The big percentage on a step is the share that reached it from the step above. * The list under a step shows how those people split (for example accepted vs rejected). * If a row is missing, nobody fell into that group on this email. * The **Sent** card and the **Sent** funnel step can disagree: the card counts everyone you targeted, the funnel step counts only eligible people. ##### What each step includes * Audience Who stayed eligible, and who was left out. * Exclusions can include previously unsubscribed, bounced, or complained; frequency capped; filtered by category; [global control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). * **Excluded: sunset policy** counts contacts with no clicks or opens for the period set in the [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts). They aren’t counted in **Sent** or **Delivered**. * **Excluded: previously bounced** is from an *earlier* send. * Bounces from *this* send show under **Deliveries**. * Sent Eligible people split into: * **Accepted by sender service:** provider took the send into work, not that it reached the inbox. * **Rejected by sender service:** see the [**Errors** tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/). * **Processing errors:** Pushwoosh-side failure. * Deliveries Delivery confirmations (not opens): confirmed vs not confirmed. * Hard and soft bounces for *this* send appear as a note under the step. Do not add or subtract them from the confirmation rows. * Below it, bounces are split by [reason](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#bounces-by-reason), hard bounces first, then soft. Each reason shows the number of addresses and their share of sent emails. Older emails don’t show this split. See [Bounces by reason](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#bounces-by-reason). * A bounce can also show in confirmed (or opened) if the provider confirmed first and bounced later. * Different from **Excluded: previously bounced** and from the **Delivered** card. * For `icloud.com`, `me.com`, and `mac.com`, the confirmed share can look lower than actual because Apple does not always return delivery confirmations. That is an Apple limitation, not a failed delivery. * Opened Opens across the full **Deliveries** step (opens can exceed confirmed deliveries). * Opens by mail scanners aren’t counted unless you turn on **Show bots** on this tab. See [Opens and clicks from mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). * First **60 days:** **Human opens** and **Machine-only opens (AMPP)**. ![Opened step for an email younger than 60 days, split into Human opens and Machine-only opens (AMPP)](https://docs.pushwoosh.com/emails-email-statistics-34.webp) * After **60 days:** the total still shows, but without the **Human opens** / **Machine-only opens (AMPP)** split. The total now includes machine-only opens indistinguishably from human opens, so do not compare or add it to the earlier split. ![Opened step for an email older than 60 days, collapsed to a single Opened row with no human/machine split](https://docs.pushwoosh.com/emails-email-statistics-35.webp) * Interactions Each person in exactly one row: * Clicked only * Clicked and unsubscribed * Clicked and complained * Unsubscribed without clicking * Complained without clicking A complaint wins over an unsubscribe. Unsubscribes and complaints can appear even when no open was tracked. How long these numbers stay The main funnel and card numbers stay for up to **one year**. After that, they are removed. Right after you send, delivery and open counts can keep updating for up to **60 days**. If a very recent email looks wrong, check again later. The numbers usually settle on their own. **After 60 days**, **Opened** still shows a total, but without the human vs machine-only split. That total includes machine-only opens indistinguishably from human opens, so do not compare or add it to the earlier split. **Clicks, unsubscribes, spam complaints, and email bounces** stay detailed for the full year. If this send also included push, after **30 days** the push delivery breakdown on that channel tab becomes less detailed (four rows → two). A message created automatically by your app or by Pushwoosh, without the Control Panel or the API, does not get a Message stats page. ##### Opens and clicks from mail scanners Mail security programs and link previews, such as Microsoft Defender for Office 365, Proofpoint, or Mimecast, open emails and click links before the recipient does. By default, Pushwoosh doesn’t count these opens and clicks, so **Opened** and **Clicked** show what recipients did. The same applies to open and click rates on [Project overview](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#email-tab) and [dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#how-to-count-mail-scanner-opens-and-clicks). To count mail scanner opens and clicks, for example to compare Pushwoosh numbers with a report that counts mail scanners, such as your email provider’s: 1. Open the **Overview**, **Timeline**, or **Clicks** tab. 2. Turn on **Show bots** at the top of the tab. Turn on **Show bots** separately on each tab where you need these numbers. It turns off again when you reopen the page. Older emails and Apple Mail Privacy Protection Opens and clicks of older emails can still include scanners, so their rates can look higher than for recent emails. Apple Mail Privacy Protection opens aren’t scanner opens and stay counted. **Show bots** also applies when you export a list of recipients or create a segment from the tab. With it on, the list or segment includes recipients whose open or click came only from a mail scanner. This applies to: * **Overview:** **Export list** from a card and **Create segment** from the **Opened** or **Clicked** card. * **Clicks:** the download icon on a link row. ### Clicks tab Use the **Clicks** tab to see which links in this email got clicks. **Link clicks** lists them by click count, sorted by clicks (highest first). ![Clicks tab Link clicks table listing each URL with click counts and export icons](https://docs.pushwoosh.com/emails-email-statistics-29.webp) * A row is one URL, without its query string. Two links to the same URL, even with different anchor text, combine into one row. Expand a row to see it broken down by query parameters. * Click the download icon on a row to export that link’s click list as CSV. * Clicks by mail scanners aren’t counted unless you turn on **Show bots** on this tab. See [Opens and clicks from mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). #### Click map Next to the **Link clicks** table, this tab also shows a click map: the email preview with its links shaded by their share of clicks, darker links getting more of them. The map shows on a single message’s stats and on an [aggregated campaign](https://docs.pushwoosh.com/product/statistics-and-analytics/aggregated-messages/) view alike, including a campaign that a journey point sends to. On an aggregated view, the map pools clicks from every message the campaign sent onto that one preview. ![Click map next to the Link clicks table, with the pricing link outlined and its click count shown in a badge](https://docs.pushwoosh.com/emails-email-statistics-33.webp) * Click a shaded link on the map to select it: the link gets a blue outline and its exact click count appears in a badge next to it. * Click a row in the **Link clicks** table to select the same link the other way: the row highlights gray, and the matching link’s badge appears on the map. * Toggle **All clicks** above the map to switch between unique and total clicks. * A link the map can’t place on the current template (removed or changed since some of the counted clicks happened) is left off the preview. A caption above the map states how many clicks and links that affects. * When several identical links pool into one badge, a separate caption above the map states how many badges that affects. Note Pushwoosh tracks clicks by URL, not by which element on the page you clicked, and ignores the query string. If a template has more than one link with the same URL (for example two “Shop now” buttons pointing at the same page), their clicks are counted together as one entry, on the map and in the **Link clicks** table alike. Pushwoosh can’t tell those elements apart. ### Timeline tab Use the **Timeline** tab to see how this email performed hour by hour (or day by day), not only the totals on Overview. For example, you can spot when opens came in after you sent, or when rejects piled up. The chart has four lines: * **Accepted by sender service:** the email provider took the message into work. * **Rejected by sender service:** the provider rejected the send. * **Delivery confirmations:** the mailbox provider reported delivery. This is not an open. * **Opens:** someone opened the email. Opens by mail scanners aren’t counted unless you turn on **Show bots** on this tab. See [Opens and clicks from mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). A summary table under the chart totals each metric for the selected window. Note For messages younger than **30 days**, the chart does not put every event at the same kind of time: * **Accepted** and **Rejected** use the time you sent. * **Delivery confirmations** and **Opens** use the time the provider or recipient responded. So a confirmation can show up much later than the send. Do not treat the four lines in one hour or day as a funnel. For the funnel, use **Overview**. For messages older than 30 days, all four lines share the same time axis. ![Timeline tab Metrics over time chart and summary table for an email with Accepted, Rejected, Delivery confirmations, and Opens](https://docs.pushwoosh.com/emails-email-statistics-30.webp) To configure the chart: 1. Set the **time range**. Times are UTC. For a window under a day, the chart plots points by the minute. For a window from a day up to a month, it groups points by the hour. For longer windows, it groups by day (or a larger step). 2. Choose **Compare metrics** to plot the four lines together, or **Compare platforms** to compare platforms for one metric. 3. Use **All platforms** to keep every platform, or pick one platform to filter the chart and table. These controls apply to the chart, the summary table, and the **Events** table below. They do not change the Overview cards or funnel. #### Custom events after a link click in this email The **Events** table under the chart shows the custom events your app or website sent after recipients clicked a link in this email. It fills in only if your app or website sends events to Pushwoosh. Each event goes to the message the user last clicked within the 72 hours before the event: * **What counts as a click:** A click on a link in the email. Opening the email and clicking the unsubscribe link don’t count. * **Clicks on other messages:** Clicks on all messages of the same app count, so an event that follows a click on a later message goes to that later message, not to this one. * **Limits:** One event goes to one message only. ![Events table under the email Timeline chart with the last click attribution line, Event, Hits, and Unique columns](https://docs.pushwoosh.com/emails-email-statistics-36.webp) | Column | What it shows | | :--------- | :--------------------------------------------------------------------------------------------------------------------------------------------- | | **Event** | The event name. | | **Hits** | How many events went to this email. Every event the user sends after the click counts, until the user clicks another message or 72 hours pass. | | **Unique** | How many distinct users sent the event at least once. | Events are matched to the message by [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/), not by device. If an email address is registered under a different User ID than the one your app or website uses for the same person, that person’s events show as 0 here. The time range filters the events, not the clicks. A click before the start of the range still counts for an event inside the range. Each row has an export icon. Click it to download the devices behind that row as CSV. The file follows the same last-click rule, so it matches the row. Tip If the table is empty, no custom events followed link clicks in this email in the selected period, or your app or website doesn’t send events to Pushwoosh. You cannot turn event tracking on from Message stats. Ask your development team whether the app or website sends [custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) to Pushwoosh. ### Bounces and delivery errors The **Errors** tab shows why this email failed for some recipients, bounces grouped by reason, and what each email error means. [Learn more ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/) ### Content panel The right-hand **Content** panel shows the email subject line, from name, and from email, above a preview of the message as recipients saw it. Click **Open preset** to view or edit the [email template](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#configure-content). **Delivery and settings** below the preview shows how this send was configured. * **Status:** whether this send finished (**Done**) or is still processing * **Sent date:** when this message actually went out * **Scheduling:** [immediate](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#send-immediately) or [selected time](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#selected-time) * **Target segment:** the [audience](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#send-to-segment) you set. Click the segment link to open or edit it ![Content panel showing email subject, from fields, preview, and Delivery and settings](https://docs.pushwoosh.com/emails-email-statistics-32.webp) ### Copy the message code To get an ID for Support or your developers when tracing this email: 1. At the top of the page, click **Message code**. 2. The ID is copied to your clipboard. You usually do not need it to read the stats. ### Export recipients and create a segment #### Export recipients Exporting recipients requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. To download recipients as CSV from the top of the page: 1. Click **Export stats**. 2. Confirm the download. To export a specific group instead: * On **Overview**, open the three-dot menu (⋮) on a card, then click **Export list**. * On the **Clicks** tab, click the download icon on a link row. * On the **Errors** tab, click the download icon on an error row. Both **Export stats** and **Export list** pull from the same per-recipient log, which covers only the last **30 days**. Older recipients are not in the file. The confirmation dialog states this limit before the download starts. #### Create a segment from Sent, Delivered, Opened, or Clicked Use this when you want to email the same people again, for example everyone who opened or clicked this email, or everyone the provider accepted for sending. 1. On **Overview**, find the **Sent**, **Delivered**, **Opened**, or **Clicked** card. 2. Click the three-dot menu (⋮) on that card. 3. Click **Create segment**. 4. In the **Create segment** dialog, enter a name in **Segment name**. 5. Click **Create**. The new [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) includes the people on that card. You can select it later when you [send another email](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/#send-to-segment). ## Gain insights into email performance trends ### Project overview To assess the overall performance of your email campaigns, open **Project overview** in the left menu and go to the **Email** tab. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#email-tab) ### Dashboards To analyze the performance of your email campaigns in detail, use pre-built **Emails and Application dashboards** in the **Statistics > Dashboards** tab or create your custom dashboard for deeper analysis. ##### Email dashboard The **Email** dashboard provides you with an in-depth analysis of your email campaign performance. It displays various metrics such as the number of subscribers, email sends, opens, deliveries, and more. ![Email dashboard showing comprehensive analysis of email campaign performance with subscriber and engagement metrics](https://docs.pushwoosh.com/emails-email-statistics-19.webp) Also, you can customize the dashboard to fit your needs by adding or removing specific metrics, or create a custom dashboard entirely tailored to your campaigns. [Learn how to do this here](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/) ##### Application dashboard The dashboard enables you to compare your email performance with other channels, providing insights into metrics such as recipients, subscribers, daily and monthly active users, etc., broken down by channel. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#dashboards-list) ## Additional resources * [Email statistics FAQ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/#email) * [Email analytics dashboard](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#preconfigured-dashboards) * [Email metrics glossary](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#email) * [How to increase your email open and click-through rates](https://www.pushwoosh.com/blog/increase-email-open-and-click-through-rates/) * [Scaling emails with Pushwoosh: Growing from 1M to 8M emails MoM](https://www.pushwoosh.com/blog/scaling-emails-with-pushwoosh/) --- # Bounces and delivery errors **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/ See why an email failed for some recipients, which bounce reasons the mail servers gave, and what each email error type means. --- Use the **Errors** tab to see why this email failed for some recipients. The red badge on the tab shows the total of rejected sends plus **Processing errors**. To get there, open the [detailed statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#detailed-email-statistics) of an email and click **Errors**. This page covers what is specific to email. For the error groups shared by every channel, see [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/). ## Delivery errors by platform **Delivery errors by platform** shows each platform (for email, **@ Email**) with sent count, failed count, and error rate. * Expand a platform to see each error code, a short description, and the count. * Click **Export errors** to download the full list, or use the download icon on one error row. Both exports require the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. * For email, the **Export errors** file also lists bounced addresses, one row per address with its last bounce on this email. For these rows, `reason` holds the [bounce reason](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#bounces-by-reason), and the `bounce_type` (`hard` or `soft`), `smtp_code`, and `provider` columns at the end of the file are filled in. The button is available even when the email has bounces but no sending errors. A file for one error row doesn’t include bounces. ![Errors tab Delivery errors by platform for Email with Illegal Email Address error expanded](https://docs.pushwoosh.com/emails-email-statistics-31.webp) ## Bounces by reason The **Bounces** block groups the addresses that bounced on this email by the reason the recipient’s mail server gave. Hard and soft bounces are listed separately, the largest reason first. The block appears only when the email has at least one bounce with a reason. If the block is missing, check the error rows above. Emails sent before October 8, 2026 The breakdown covers bounces received from October 8, 2026. Older emails don’t show the **Bounces** block or the breakdown under [**Deliveries**](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#what-each-step-includes). The block shows these columns: | Column | What it shows | | :-------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Reason** | The bounce reason. | | **Addresses** | How many addresses got this reason. Each address counts once, by its last bounce on this email. | | **Top SMTP codes** | Up to three SMTP codes behind this reason (status codes the recipient’s mail server returns, such as `5.1.1`), the most common first, with the number of addresses for each. | | **Provider** | The service that returned the response shown in **Provider response**. | | **Provider response** | The response most of these addresses got. Hover it to read the full text. Recipient addresses in it are masked. | The table below explains each reason and what to do about it. | Reason | What it means | What to do | | :----------------------------- | :-------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Address doesn’t exist** | The recipient’s mail server says the address doesn’t exist. | Remove it from your list. Reactivating it won’t help. | | **Mailbox inactive** | The mailbox is disabled. | Remove it from your list. | | **Mailbox full** | The mailbox is over quota. This is usually temporary. | Nothing. The address may work again later. | | **Blocked: sender reputation** | The provider limits your sending domain or IP address. | Lower the sending volume, check your complaint rate, and check SPF, DKIM, and DMARC for your [sending domain](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/). | | **Rejected as spam** | The provider rejected the email as spam. | Check the content and links. | | **Delivery failed** | Delivery timed out because the recipient’s server could not be reached, for example because of a connection or DNS problem. | Watch for repeat failures. | | **Other** | Pushwoosh couldn’t classify the provider’s response. | Check the SMTP code and the provider response. | The same reason can appear in both groups. For example, a full mailbox can return a hard or a soft bounce, depending on the provider. Whether Pushwoosh suppresses the address depends on the bounce type, not on the reason. See [Email error types you may see](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/bounces-and-errors/#email-error-types-you-may-see). ## Email error types you may see The tables below explain the bounce, complaint, and suppression names that can appear when you expand **@ Email**. For sender and SMTP codes, see [Email errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/email-errors/). * Rejected by recipient's mail server Pushwoosh sent the email, but the recipient’s mail server refused it: a hard bounce, a soft bounce, a spam complaint, or an undetermined bounce. For hard bounces and complaints, Pushwoosh automatically suppresses the address so it won’t be emailed again. Soft bounces and undetermined bounces don’t suppress the address. | **Error** | **Description** | **What happens next** | | ----------------------------------- | -------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Hard Bounce General** | Permanently rejected without a specific reason. | Pushwoosh adds the address to your suppression list. It won’t be emailed again. If the hard bounce rate exceeds your plan’s [hard bounce rate limit](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/#sending-suspension), 5% by default, Pushwoosh pauses email sending in the project and checks your email database. | | **Hard Bounce Mailbox Full** | The inbox is over quota and the server rejected it permanently. | Pushwoosh adds the address to your suppression list. | | **Hard Bounce Inactive Mailbox** | The mailbox is closed or abandoned. | Pushwoosh adds the address to your suppression list. | | **Hard Bounce Suppressed** | The receiving provider already has this address suppressed after earlier permanent failures. | Pushwoosh adds it to your suppression list as well. | | **Hard Bounce Undetermined** | Permanently rejected for an unknown reason. | Pushwoosh adds the address to your suppression list. | | **Soft Bounce General** | Temporarily rejected without a specific reason. | Nothing is suppressed. Pushwoosh retries on the next send. | | **Soft Bounce Attachment Rejected** | The recipient’s server blocked the email because of attachment restrictions. | Nothing is suppressed. Try sending without the attachment. | | **Soft Bounce Spam** | The recipient’s server flagged the email as spam. | Nothing is suppressed. Review your content and sender authentication. | | **Soft Bounce Message Too Large** | The email exceeded the recipient’s size limit. | Nothing is suppressed. Reduce image sizes or simplify the template. | | **Soft Bounce Content Rejected** | The recipient’s server rejected the message content. | Nothing is suppressed. Review the email for spam triggers and policy violations. | | **Soft Bounce Mailbox Full** | The recipient’s inbox is full right now. | Nothing is suppressed. Delivery may succeed once space frees up. | | **Soft Bounce Undetermined** | Temporarily rejected for an unknown reason. | Nothing is suppressed. Watch for repeat failures. | | **Soft Bounce Timeout** | The recipient’s server took too long to respond. | Nothing is suppressed. Pushwoosh retries on the next send. | | **Complaint General** | A complaint was reported for this address. | Pushwoosh unsubscribes the recipient and permanently suppresses the address. | | **Complaint Abuse** | The recipient marked the email as spam. | Pushwoosh unsubscribes the recipient and permanently suppresses the address. Complaints hurt sender reputation the most. Keep the complaint rate below 0.1% of sent emails, the level that mailbox providers such as Gmail expect. If it reaches your plan’s [complaint rate limit](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/#sending-suspension), 1% by default, Pushwoosh pauses email sending in the project and asks you to check email contents. | | **Bounce Undetermined** | The server accepted the email but never confirmed delivery. The outcome is unknown. | No address is suppressed. Monitor for repeat occurrences. | To keep the bounce rate down * Export bounced addresses from the **Errors** tab (or clean your list using your usual process) and remove them from future sends. * See [Email deliverability best practices](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/). * For an automated export, share the [bounced emails API](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#bouncedemails) with your developers. * Not sent (address suppressed) Pushwoosh didn’t send these emails at all. The address was already on your suppression list from an earlier bounce, complaint, or unsubscribe, or the address itself is invalid. | **Error** | **Description** | | ------------------------- | ------------------------------------------------------------------------------------------------------ | | **Illegal email address** | The address isn’t a valid email format, so it was rejected before sending. | | **Email is bounced** | The address is suppressed after an earlier permanent bounce. | | **Email complaint** | The address is suppressed after an earlier spam complaint. | | **Email is unsubscribed** | The recipient opted out of marketing emails. Transactional emails are still delivered to this address. | To learn how to prevent these issues and improve deliverability, see [Email deliverability best practices](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/). --- # How to read the Errors tab **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/ Understand what each error group on the Message stats Errors tab means, and what you should check or whom to ask about it next. --- When some recipients didn’t get your message, the **Errors** tab shows why. It lists the failed sends of the message, so you can [look up what each error means](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#look-up-an-error) and fix it, or [export the failed sends](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#export-errors). To open the tab, go to the Message stats page of the message and click **Errors**, next to **Overview** and **Timeline**. ## What the Errors tab shows The tab shows the failed sends of one channel. If a message went out on several channels, the tab shows the channel you selected above it. Failed sends are grouped by platform, for example iOS, Android, or Email. Click a platform to collapse or expand its errors. Each group has these parts: * **Platform row:** how many messages were sent on this platform, how many failed, and what share of the channel’s eligible audience they are. * **Error rows:** one row per error, shown as its name and code, for example **Invalid To Phone Number (1001)**, with a short description under it. Each row has its own count and its share of the same audience. ![Delivery errors by platform showing iOS, Android, and Huawei error breakdowns with counts and percentages](https://docs.pushwoosh.com/statistics-push-notification-statistics-18.webp) The red badge on the **Errors** tab is the number of failed sends in the selected channel, **Processing errors** included. Without failed sends there is no badge. ### When the tab is empty The tab shows **No delivery errors for this message** in two cases: * **Nothing failed.** There are no errors to list. * **The message failed before it was sent** (status **FAIL**). Nothing was sent, so there is nothing to list. It doesn’t mean the message was delivered. The reason is shown on the **Overview** tab. See [Why a message failed](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed). ### What the tab doesn’t show The tab lists only sends that failed before or while Pushwoosh handed them to the delivery platform. It doesn’t list: * **Audience exclusions.** Subscribers left out on purpose, for example by frequency capping, the global control group, or an earlier unsubscribe. The **Overview** tab shows them in **Excluded** rows. * **Rejections after the delivery platform accepted the message.** For example, an SMS that the carrier fails to deliver later. Such a send counts as **Accepted by gateway** on the **Overview** tab and, until delivery is confirmed, as **Delivery not confirmed**. * **Temporary failures that Pushwoosh retries.** They aren’t recorded as errors. ## Look up an error To find out what an error means and how to fix it: 1. Read the name and the code in the error row. 2. Look for them in [Errors on every platform](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform). 3. If they aren’t there, open the page for the row’s platform under [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/). 4. If the row has a category name instead of a code, for example **Unclassified error**, find it in [Error categories](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#error-categories). Pushwoosh sorts every error into one of these categories. The tab shows a category name only for errors that have no code. ## Errors on every platform These errors can appear on any platform, from push to SMS and messengers. Pushwoosh reports them before or while handing the message to the delivery platform. | Error | What it means | What to do | | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | | **Push Token Not Found** (1007), **Phone Number Not Found** (1007) on SMS | The subscriber has no push token, phone number, or chat ID for this platform in the project, so Pushwoosh didn’t send the message. | Collect the contact and [register](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/) it in the project. | | **Platform Is Disabled** (10005) | The platform is turned off for the project. | Turn the platform on in **Settings > Configure Platforms**. If you don’t want to send on this platform, remove it from the message. | | **Skip Push** (1011) | A placeholder had no value for this subscriber and no default value. On SMS, and for text built from a product feed, it also appears when the text was empty after placeholders were filled in. | Check the placeholders and their default values in the message or preset. | | **Unable To Compile Liquid Template** (1014) | The message contains a Liquid syntax error. | Fix the Liquid template in the message or preset. | | **Internal Service Error** (10006) | The send failed before or while Pushwoosh handed it to the delivery platform. On some platforms it also covers setup errors, see the platform’s page under [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/). | If it repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | ## Error categories Each category below says what went wrong and what to do. | Category | What it means | What to do | | ---------------------------------- | ----------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Invalid or expired token** | The token or email address can no longer receive messages, for example because the app was deleted or the address is invalid. | Common on large audiences. Compare with earlier sends of a similar size. Dig deeper only if the share jumps a lot. | | **No push token** | The subscriber has no push token, email address, phone number, or chat ID on this platform. | For push, ask your development team to check that the app registers devices. For other channels, collect and register the contact. | | **Device not found** | The device is not in your Pushwoosh audience. | Often follows segment filters or audience cleanup. Check who was targeted. | | **Platform disabled** | This platform is turned off in the project, or the delivery platform’s API is disabled, for example Firebase Cloud Messaging. | Turn the platform on in app configuration, or remove it from the send. | | **Provider quota exceeded** | Apple, Google, or another delivery platform limited the send, or your email sending quota is used up. | Slow down or split the send. Check with Support if it keeps happening. | | **Invalid message content** | The message content was rejected (empty or invalid). | Open the preset or message text and fix empty or broken content. | | **Invalid platform configuration** | App credentials for this platform are wrong or outdated (certificates, keys, and similar). | Check platform setup in the Control Panel, or ask Support / your developers to renew credentials. | | **Provider error** | The delivery platform reported a problem, or the code was not recognized. | A few cases are usually fine. Many cases: contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message date and **Message code**. | | **Processing error** | Something failed in Pushwoosh’s own processing while sending. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **Unclassified error** | The error did not fit a known group. | Should be rare. If you see many, contact Support with the message date and **Message code**. | For SMS, Viber, WhatsApp, Telegram, Facebook Messenger, Kakao, and LINE, every error the provider returns is listed under **Provider error**, whatever its cause, including sending limits, invalid credentials, and template problems. **Provider quota exceeded** and **Invalid platform configuration** apply only to push and email. To find out what such an error means, look up its name and code in [SMS and messenger errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/). ### Processing errors **Processing errors** are failures in Pushwoosh’s own processing: building the message, handing it over, or reading the delivery platform’s reply. You see them in two places: * The **Errors** tab lists them together with other errors. * On the **Overview** tab, the funnel shows them in their own row, not under rejected sends. Note A row named **Processing Error** with a code in brackets is a different thing: a failure before sending that has no name of its own. Its code can belong to any category. For example, a WhatsApp message outside the 24-hour window shows this way. ## Export errors You can export one error or all errors on the tab: * To export recipients for one error, click the **download icon** next to that error. * To export every error in one file, click **Export errors**. Exporting errors requires both of these: * The [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. * Access to message logs on your account. If the download icon and **Export errors** are missing, ask your account admin or Support to check both. Rows without a code have no download icon. Use **Export errors** to include them. --- # In-app statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/ Analyze in-app messaging performance with Pushwoosh in-app analytics. Track impressions, interactions, and engagement trends. --- Pushwoosh provides in-app analytics at both the individual message and overall campaign level, giving you deep insights into channel performance. ## Track individual in-app message performance directly from the Journey canvas You can monitor the performance of each in-app message within a customer journey directly on the **Journey canvas**. To view the statistics for a message, open [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) and turn on **Show metrics**, or hover over the element to see the metrics. [](https://docs.pushwoosh.com/in-apps-in-app-statistics-1.webm "Viewing in-app message analytics from the Journey canvas in Pushwoosh") This will show the following data for an in-app message: | | | | ----------------- | ------------------------------------------------------------------------------------------------------ | | **Goals Reached** | Indicates the number of users who reached the Conversion Goal at that specific point. | | **Drop-offs** | Represents the number of messages that couldn’t be sent, such as when a user’s push token has expired. | Note By default, the data is displayed for the period since the journey launch. You can choose a different period from the panel on the left side and then click **Apply time range**. For deeper in-app analytics, double-click on the in-app element. Then, in the window that appears, click on the name of the Rich Media. This action will open up comprehensive statistics for the message. Tip You can also open the statistics page from the in-apps list: go to **Content** → **In-apps**, find the template, click the three dots next to its name, and select **Statistics**. Once the page opens, specify the timeframe for data tracking and select the platform. You can also decide whether the data will be displayed daily, hourly, weekly, or monthly. At the top of the page, you’ll find a summary of the most important in-app performance metrics: * **Impressions:** The number of times the in-app was displayed to users during the selected period. * **Unique Impressions:** The number of unique users who were shown the in-app message during the selected period. * **Interactions:** This metric reflects the number of times users engaged with the in-app message, such as button clicks or link taps. * **Unique Interactions:** The number of unique users who interacted with the in-app message during the selected period. * **Audience:** The list of active user devices on which the in-app was displayed. ![Key in-app analytics metrics](https://docs.pushwoosh.com/in-apps-in-app-statistics-2.webp) ### Key Metrics tab The **Key Metrics** tab provides a visual representation of the most important metrics that reveal the performance of your in-app message. On each graph, you can select a specific time period and examine it in more detail. * **Impressions:** Hover over the graph to view the number of times users were exposed to your in-app message during the selected period. ![The Impressions graph shows the number of times users were exposed to your in-app message during the selected period.](https://docs.pushwoosh.com/in-apps-in-app-statistics-3.webp) * **Interactions:** Navigate to the **Interactions** graph to understand how users engaged with the in-app elements. For a more granular breakdown of interactions by individual elements, visit the Interactions tab or click **Show interactions**. ![The Interactions graph showing how users engaged with the in-app elements.](https://docs.pushwoosh.com/in-apps-in-app-statistics-4.webp) * **Impression Duration:** Hover over the graph to see how long users viewed your in-app message. This graph segments users based on their impression sessions. ![The Impression Duration graph showing how long users viewed your in-app message](https://docs.pushwoosh.com/in-apps-in-app-statistics-5.webp) * **Skipped In-Apps:** Hover over the **Skipped** graph to identify the number of users who closed the in-app without interacting with it. ![The Skipped graph showing the number of users who closed the in-app without interacting with it](https://docs.pushwoosh.com/in-apps-in-app-statistics-6.webp) You can download all graphs in SVG or PNG formats for further analysis and presentation. To do so, simply click on the **Menu** icon located near each graph. ![Downloading in-app analytics charts in SVG or PNG from Pushwoosh](https://docs.pushwoosh.com/in-apps-in-app-statistics-7.webp) ### Interactions tab The **Interactions** tab allows you to dive deeper into user interactions. This tab highlights the most frequently interacted-with elements, such as close or action buttons. ![The Interactions tab](https://docs.pushwoosh.com/in-apps-in-app-statistics-8.webp) ### Events tab The **Events** tab displays data for each event that can be triggered when interacting with the in-app, including event name, platform, the total number of events triggered, and the number of unique users. ![The Events tab](https://docs.pushwoosh.com/in-apps-in-app-statistics-9.webp) ### Audience tab The **Audience** tab reveals your in-app message recipients and provides a list of active user devices on which the in-app was displayed. Click on the **HWID** to view user details. The **Audience** tab requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. ![The Audience tab](https://docs.pushwoosh.com/in-apps-in-app-statistics-10.webp) ### Usage tab The **Usage** tab shows the start date of the in-app and the total number of impressions. ![Usage tab showing start date and total impressions for in-app campaigns](https://docs.pushwoosh.com/in-apps-in-app-statistics-11.webp) ## Understand overall in-app messaging performance trends You can use the pre-built **In-app dashboard** to evaluate the overall performance of your in-app campaigns. For this, navigate to **Statistics > Dashboards > In-apps.** The dashboard provides insights into overall performance of your in-apps, featuring metrics like in-app Impressions and Interactions. ![In-app analytics dashboard showing campaign-level performance metrics](https://docs.pushwoosh.com/in-apps-in-app-statistics-12.webp) Also, you can customize the dashboard to fit your needs by adding or removing specific metrics, or create a custom dashboard entirely tailored to your campaigns. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) ## Additional resources * [In-app statistics FAQ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/#in-app-messages) --- # Messaging statistics FAQ **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/ Find answers to common questions about push, email, SMS, messenger, and in-app statistics for individual messages in Pushwoosh --- Below are answers to common questions about statistics for individual push, email, SMS/messenger, and in-app messages. For account-wide statistics questions, see [Statistics FAQ](https://docs.pushwoosh.com/product/statistics-and-analytics/statistics-faq/). ## Push notifications ### What push notification analytics does Pushwoosh provide? Pushwoosh provides analytics at multiple levels, including individual message, campaign, and overall trends in the push channel performance. It tracks key metrics such as open rate, CTR, and post-notification user behavior to help you understand how your push notifications perform across various audiences and platforms. ### What key performance metrics can I track for push notifications? Pushwoosh lets you monitor essential metrics like the number of messages sent, delivered to unique recipients, and opened, along with click-through rates (CTR). It also tracks cases where messages couldn’t be delivered, helping you optimize delivery performance. ### Can I export my push notification analytics data? Yes. Pushwoosh allows you to export campaign analytics data, including recipient lists and user interaction reports, in CSV format. ### How do push notification analytics impact campaign optimization? Push notification analytics give you actionable insights into user behavior. By identifying high-performing content, drop-off points, and engagement patterns, you can A/B test more effectively, adjust timing, and refine your targeting, all of which lead to better engagement and higher conversion rates. ## Email ### What email analytics does Pushwoosh provide? Pushwoosh provides comprehensive email analytics across multiple levels, including individual message performance, full campaign trends, and channel-wide email statistics. With built-in email dashboards and reporting tools, you can monitor how users engage with your emails and optimize based on actionable data. ### Can I export my email analytics data? Yes. Pushwoosh allows you to export detailed email analytics, including recipient lists filtered by delivery or engagement status, link click data for every message, and CSV reports for further analysis in external tools. You can also export segments based on user behavior for targeted follow-ups. ### How do email analytics improve campaign performance? Email analytics help you understand how your messages perform in real-world use, from delivery success to user engagement. Using insights from the email metrics dashboards, you can identify high-performing content, test subject lines and calls-to-action, optimize send timing and frequency, and reduce bounce and complaint rates. This data-driven approach boosts open rates, click-throughs, and conversions. ## SMS and messengers ### What SMS and messenger analytics does Pushwoosh provide? Pushwoosh tracks SMS, WhatsApp, Viber, and Telegram sends individually as a Message stats page. It shows audience reach, delivery confirmations, opens when the platform supports them, and a per-platform breakdown of send errors. ### Why doesn’t SMS show an Opened metric? SMS has no open event to track. Today only WhatsApp reports opens. Viber and Telegram do not. ### How long do SMS and messenger statistics stay available? The main funnel and card numbers stay for up to one year. Delivery and open counts can keep updating for up to 72 hours after you send. ### Can I export SMS and messenger recipient data? Yes. Use **Export stats** at the top of the page, or **Export list** from a card or an error row for that specific group. Both draw from the per-recipient log, which covers the last 30 days. Recipients outside that period aren’t included. ## In-app messages ### How can I view analytics for a specific in-app message? You can open statistics from the **Content** → **In-Apps** list by clicking the template name, or from the Journey canvas by turning on **Show metrics** in [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/) or double-clicking the in-app element and clicking the in-app’s name. The statistics page shows impressions, interactions, and audience data. ### Where can I view overall campaign-level in-app analytics? Visit the **In-app Dashboard** under **Statistics > Dashboards > In-apps** for a complete overview of your in-app messaging performance. ### Can I export my in-app analytics data? Yes. You can access detailed metrics per message and across campaigns. --- # Push notification statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics/ Track delivery, CTR, opens, and engagement using push notification analytics for your mobile messaging campaigns and optimize performance. --- With Pushwoosh, you can effortlessly track how well your push notifications are performing, both individually and overall as a channel. This guide explains how to use Pushwoosh’s push notification analytics tools to monitor performance, identify trends, and optimize messaging strategy. ## Track individual push message performance in Customer Journey You can easily track the performance of push notifications sent as part of your customer journey directly on the canvas. To view the metrics, simply hover over a message you want to track or turn on the **Show metrics** toggle in [Canvas settings](https://docs.pushwoosh.com/product/customer-journey/canvas-navigation/). [](https://docs.pushwoosh.com/statistics-push-notification-statistics-1.webm "Viewing push notification metrics on Journey canvas by hovering over message or enabling Show metrics toggle") This will display the following performance data for each push notification in your journey. | Goals reached | Indicates the number of users who reached the Conversion Goal at that specific point. | | ------------- | ------------------------------------------------------------------------------------------------------------------------- | | Opened | Displays the number of messages opened by users. | | CTR | Represents the percentage of users who clicked on (opened) the push notification after receiving it. | | Drop-offs | Represents the number of messages that couldn’t be sent for some reason (for example, if a user’s push token had expired) | Note By default, the statistics are displayed for the period since the journey launch. You can choose a different period from the panel on the left side and then click **Apply time range**. For more detailed insights into the effectiveness of a push notification, double-click on the message element on the canvas. This action will open a detailed statistics window, presenting key metrics specific to the message, including: * Total number of messages sent * Total number of messages opened * Total number of entries * The number of users who skipped this message element due to the absence of a related device. ![Detailed statistics window for push notification showing total messages sent, opened, entries, and skipped users](https://docs.pushwoosh.com/statistics-push-notification-statistics-2.webp) ## View detailed push notification statistics To access detailed statistics for a push notification, follow these steps: 1. **Via One-time messages:** * Navigate to **Campaigns > One-time messages**. * Select the message you want to analyze. * Click on the message to open the statistics window. 2. **For notifications in a Journey:** * Open the **Journey** canvas. * Select the **Push** element within the Journey. * Click **Detailed statistics** to view performance insights. ![Access detailed push notification statistics](https://docs.pushwoosh.com/statistics-push-notification-statistics-3.webp) ## Detailed push notification statistics Use **Message stats** to check how a specific send performed, from audience targeting to opens. ![Message stats page header with title and Overview, Timeline, and Errors tabs](https://docs.pushwoosh.com/statistics-push-notification-statistics-15.webp) If a message went out on more than one channel (for example, iOS push and email in one send), channel tabs appear above **Overview** / **Timeline** / **Errors** so you can pick which channel to view. A single-channel message shows no channel tabs. ### Overview tab Use the **Overview** tab to check reach, delivery, and opens for this push. If the message has the **FAIL** status, the **Overview** tab shows dashes instead of numbers and a notice with the reason it was not sent. See [Why a message failed](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed). #### Campaign performance These cards summarize how this push performed at a high level. The **Opened** card appears only when there’s open data for this send. Hover the ⓘ on any card to see the exact formula. ![Campaign performance section showing Audience, Sent, and Opened summary cards with counts and percentages](https://docs.pushwoosh.com/statistics-push-notification-statistics-20.webp) | Card | What it shows | | :----------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Audience** | Total audience devices for this send, and how many stayed eligible after exclusions (for example frequency capping or a control group). | | **Sent** | Share **of total audience** that Apple, Google, or another platform accepted for sending, and that device count. Accepted means the platform took the send into work, not that the notification already appeared on the device. | | **Opened** | Share **of sent** devices that opened the notification at least once, and that device count. | #### Campaigns distribution **Campaigns distribution** breaks this push into four steps: * **Audience:** who you targeted * **Sent:** who was accepted for sending * **Deliveries:** who got a delivery confirmation * **Opened:** who opened You can view the same breakdown in two ways: as a funnel (default) or as a table. Use the view dropdown in the block header to switch between **Funnel view** and **Table view**. The table has one column per platform plus an **All platforms** summary column (hidden when there’s only one platform). The percentage column from the funnel view is not shown in the table. Click **Refresh** to reload the numbers. Next to it, a timestamp like **at 14:05 UTC · Sep 16, 2026** shows when stats last loaded. Note The funnel and the cards above always show the full history of this push. The date range on **Timeline** does not change these numbers. ![Campaigns distribution funnel showing Audience, Sent, Deliveries, and Opened steps with row breakdowns and bar charts](https://docs.pushwoosh.com/statistics-push-notification-statistics-21.webp) ##### How to read the funnel * Follow the path down: **Audience → Sent → Deliveries → Opened**. * The big percentage on a step is the share that reached it from the step above. * The list under a step shows how those devices split (for example accepted vs rejected). * If a row is missing, nobody fell into that group on this push. * The **Sent** card and the **Sent** funnel step can disagree: the card counts everyone you targeted, the funnel step counts only eligible devices. ##### What each step includes * Audience Who you targeted, split into eligible devices and who was left out by [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) or a global control group. * Sent Eligible devices split into: * **Accepted by gateway:** Apple, Google, or another platform took the push into work. This is not the same as the notification appearing on the device. * **Rejected by gateway:** a platform or token problem. Details are on the **Errors** tab. * **Processing errors:** a Pushwoosh-side failure. If this share is high, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). [Learn more about sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) * Deliveries Which accepted devices confirmed receipt. Confirmed means the device or platform reported delivery, not that the person saw the notification. * **Mobile push younger than 30 days:** four rows * **Displayable: confirmed by device** * **Displayable: no confirmation** * **Not displayable: confirmed by device** * **Not displayable: no confirmation** Displayable means notifications were allowed on the device at send time. The push could have appeared. It does not mean the person saw it. ![Deliveries step for a push younger than 30 days, with the four-row Displayable/Not displayable confirmed-by-device breakdown](https://docs.pushwoosh.com/statistics-push-notification-statistics-23.webp) * **Web push**, or **mobile push older than 30 days:** two rows (confirmed / not confirmed). After 30 days, devices with notifications off are included in the totals. A note under the step explains this. ![Deliveries step for a push older than 30 days, collapsed to two rows: confirmed by device and not confirmed](https://docs.pushwoosh.com/statistics-push-notification-statistics-24.webp) * **Breakdown not available:** a note under the step says so. Step totals still show. * **Lists or dashboards show zero:** on some accounts delivery can look like zero outside Message stats, while this funnel and **Timeline** still show numbers. Trust the Message stats funnel for that message. * Opened Devices that opened the notification. Opens are tracked across the full **Deliveries** step, so the opened count can be higher than confirmed deliveries if an open is recorded without a delivery confirmation. If the push has [buttons](https://docs.pushwoosh.com/product/content/push-presets/#add-buttons-to-the-notification), **Button clicks** lists how many devices tapped each button. A tap on a button counts as an open. A device can tap more than one button, so the sum of the button rows can differ from the total. A button nobody tapped isn’t listed. How long these numbers stay The main funnel and card numbers stay for up to **one year**. After that, they are removed. Right after you send, delivery and open counts can keep updating for up to **1 day**. If a very recent push looks wrong, check again later. The numbers usually settle on their own. A message created automatically by your app or by Pushwoosh, without the Control Panel or the API, does not get a Message stats page. ### Timeline tab Use the **Timeline** tab to see how this push performed hour by hour (or day by day), not only the totals on Overview. For example, you can spot when opens came in after you sent, or when rejects piled up. The chart (**Metrics over time**) has four lines: * **Accepted by gateway:** the delivery platform (Apple, Google, or another) took the push into work. * **Rejected by gateway:** the platform rejected the send (for example a token or configuration problem). * **Delivery confirmations:** the device or platform reported that the push was delivered. This is not the same as the person seeing it. * **Opens:** someone opened the notification. A summary table under the chart totals each metric for the selected window. Note For messages younger than **30 days**, the chart does not put every event at the same kind of time: * **Accepted** and **Rejected** use the time you sent. * **Delivery confirmations** and **Opens** use the time the device answered. So a confirmation can show up much later than the send. Do not treat the four lines in one hour or day as a funnel. For the funnel, use **Overview**. For messages older than 30 days, all four lines share the same time axis. ![Metrics over time chart with Accepted by gateway, Rejected by gateway, Delivery confirmations, and Opens plotted across a multi-day send](https://docs.pushwoosh.com/statistics-push-notification-statistics-17.webp) To configure the chart: 1. Set the **time range** (for example **All time**, or a shorter window). Times are UTC. For a window under a day, the chart plots points by the minute. For a window from a day up to a month, it groups points by the hour. For longer windows, it groups by day (or a larger step). 2. Choose **Compare metrics** to plot the four lines together, or **Compare platforms** to compare platforms for one metric. 3. Use **All platforms** to keep every platform, or pick one platform to filter the chart and table. These controls apply to the chart, the summary table, and the **Events** table below. They do not change the Overview cards or funnel. #### Custom events after a tap on this push The **Events** table under the chart shows the custom events your app sent after users tapped this push. It fills in only if your app sends events to Pushwoosh. Each event goes to the message the user last clicked within the 72 hours before the event: * **What counts as a click:** A tap that opens the notification. * **Clicks on other messages:** Clicks on all messages of the same app count, so an event that follows a tap on a later message goes to that later message, not to this one. * **Limits:** One event goes to one message only. ![Events table under the Timeline chart with Event, Hits, and Unique columns and an export icon per row](https://docs.pushwoosh.com/statistics-push-notification-statistics-19.webp) | Column | What it shows | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------ | | **Event** | The event name. | | **Hits** | How many events went to this push. Every event the user sends after the tap counts, until the user clicks another message or 72 hours pass. | | **Unique** | How many distinct users sent the event at least once. | Events are matched to the message by [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/), not by device. The Timeline controls change the table in two ways: * **Time range:** filters the events, not the taps. A tap before the start of the range still counts for an event inside the range. * **Platform:** filters the taps by the platform of the push. The event itself can come from any platform, for example a purchase on your website after a tap on an Android push. Each row has an export icon. Click it to download the devices behind that row as CSV. The file follows the same last-click rule, so it matches the row. Tip If the table is empty, no custom events followed taps on this push in the selected period, or your app doesn’t send events to Pushwoosh. You cannot turn event tracking on from Message stats. Ask your development team whether the app sends [custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) to Pushwoosh. ### Errors tab Use the **Errors** tab to see why sends were rejected. See [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) for what the tab shows, and [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) for what each error code means. ### Content panel The right-hand **Content** panel shows the notification as recipients saw it. Use it to verify message content and confirm delivery settings. * To view or edit the push preset, click **Open preset**. See [Push presets](https://docs.pushwoosh.com/product/content/push-presets/). * If the message was sent in more than one language, use the chips below the preview to switch between them. **Delivery and settings** below the preview shows how this send was configured. * **Status:** whether this send finished (**Done**) or is still processing * **Sent date:** when this message actually went out * **Scheduling:** [immediate](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#send-immediately), [selected time](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#selected-time), or [Best time](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#best-time-paid-subscription-required) * **Target segment:** the [audience](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#send-to-segment) you set * **Target platforms:** the [platforms](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#target-platforms) selected, for example iOS, Android, or Web Click **Show all properties** only if you need technical details. [**Request ID**](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id) is useful for Support or your developers when tracing this exact send. Journey and automated sends often have no Request ID. Skip it when you are only checking performance. ![Content panel showing notification preview with Open Preset link, and Delivery and settings with status, sent date, scheduling, segment, and platforms](https://docs.pushwoosh.com/statistics-push-notification-statistics-22.webp) ### Copy the message code To get an ID for Support or your developers when tracing this push: 1. At the top of the page, click **Message code**. 2. The ID is copied to your clipboard. You usually do not need it to read the stats. ### Export recipients and create a segment #### Export recipients Exporting recipients requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. To download recipients as CSV from the top of the page: 1. Click **Export stats**. 2. Confirm the download. You can also click **Export list** from: * A card’s three-dot menu (⋮) on **Overview** * An error row on the **Errors** tab * An event row in the **Events** table on **Timeline** (export icon) Both **Export stats** and **Export list** pull from the same per-recipient log, which covers only the last **30 days**. Older recipients are not in the file. The confirmation dialog states this limit before the download starts. #### What the file contains The file has one row per recipient device: | Column | Meaning | | -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `hwid` | [Device identifier](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#hwid) | | `user_id` | [UserID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) of the device, if it is set | | `platform` | Device platform, for example iOS, Android, or Chrome | | `send_timestamp` | When Pushwoosh handed the push to the platform provider (APNs, FCM, and so on) | | `delivery_timestamp` | When the device confirmed delivery. Empty if delivery is not tracked for that platform | | `open_timestamp` | When the recipient opened the push. Empty if they did not open it | | `send_error` | Name of the error that stopped the send. Empty for a successful send | | `pae` | `false` only for an Unshowable send (see below). `true` for every other outcome, including a row with no send at all — it isn’t a live read of the device’s permission state | | `send_status` | Result of the send: `Success`, `Unshowable`, or the error name | **Send statuses** * `Success`: the provider accepted the push, and the device can show it. * `Unshowable`: the provider accepted the push, but the recipient has notifications turned off in the OS settings, so no banner appears. This is not a delivery failure: the message still reaches the app, and if the send has [Save message to Inbox](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#save-message-to-inbox) enabled, the recipient can still find and open it in [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/). Pushwoosh counts unshowable sends as successful sends and reports them separately as **Unshowable sends**. * Any other value is the provider’s specific rejection reason, for example `Unregistered` for an iOS token that is no longer valid. The CSV spells each name with spaces (`Frequency Capping`). The platform pages under [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) use the same name without spaces (`FrequencyCapping`). Match the row’s `platform` column to the right page for the full list of codes and what to do about them. Tip To reward or re-target only the users who can currently see a banner, build a [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) on the **Push Alerts Enabled** [default tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) set to `true`. It closely tracks the `pae` column, but the tag is the device’s current permission state, while `pae` is a snapshot from one past send. #### Create a segment from Sent or Opened Use this when you want to message the same devices again — for example, everyone who opened this push, or everyone the platform accepted for sending. 1. On **Overview**, find the **Sent** or **Opened** card. 2. Click the three-dot menu (⋮) on that card. 3. Click **Create segment**. 4. In the **Create segment** dialog, enter a name in **Segment name**. 5. Click **Create**. The new [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) includes the devices on that card. You can select it later when you [send another push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#send-to-segment). The **Audience** card has **Export list** only — not **Create segment**. ## Understand the overall performance of push notifications You can view overall push notification analytics in the Dashboards section to assess trends in engagement and reach. For this, navigate to **Statistics > Dashboards > Push notifications** The dashboard provides comprehensive insights into the overall performance of push notifications, featuring metrics like: * Recipients * Push CTR * Push sends * Push deliveries * Push opens * Installs * Uninstalls ![Push notifications dashboard showing comprehensive analytics with recipients, CTR, sends, deliveries, opens, installs, and uninstalls](https://docs.pushwoosh.com/statistics-push-notification-statistics-14.webp) Also, you can customize the dashboard to fit your needs by adding or removing specific metrics, or create a custom dashboard entirely tailored to your campaigns. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/metrics/) ## Additional resources * [Push notification statistics FAQ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/#push-notifications) * [Push analytics dashboard](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#preconfigured-dashboards) * [Push notification benchmarks (from Pushwoosh 2025 study)](https://www.pushwoosh.com/blog/push-notification-benchmarks/) * [How to increase push notifications CTR](https://www.pushwoosh.com/blog/increase-push-notifications-ctr/) * [From guesswork to experimentation: Increase your ROI with push notification A/B/n testing](https://www.pushwoosh.com/blog/push-notification-a-b-testing/) --- # SMS and messenger statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/ Track delivery, opens, and errors for SMS, WhatsApp, Viber, and Telegram messages using detailed Pushwoosh message stats. --- Pushwoosh tracks SMS, WhatsApp, Viber, and Telegram sends the same way it tracks push and email: as a **Message stats** page you open for one sent message. Open it from **Campaigns → [One-time messages](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/)**, or from **Detailed statistics** on the message element in a Journey. Click the message name to open its stats. ## Detailed SMS and messenger statistics Use **Message stats** to check how a specific SMS or messenger send performed. The page has three tabs: **Overview**, **Timeline**, and **Errors**. If the same send also went to other channels, channel tabs appear above **Overview** / **Timeline** / **Errors**, labeled by channel (for example **SMS** or **WhatsApp**). A single-channel message shows no channel tabs. ### Overview tab Use the **Overview** tab to check reach, delivery, and opens (when the platform reports them) for this send. If the message has the **FAIL** status, the **Overview** tab shows dashes instead of numbers and a notice with the reason it was not sent. See [Why a message failed](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/#why-a-message-failed). #### Campaign performance Which cards you see depends on the channel: * **Always:** **Audience** and **Sent** * **Opened:** only when the platform reports opens. Today that is WhatsApp. Viber and Telegram do not show it. SMS never shows it: SMS has no open event to track. * **Read (RCS):** only for an SMS send where at least one delivery was confirmed over [RCS](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/). Hover the ⓘ on any card for the exact formula. ![Campaign performance section showing Audience and Sent summary cards with counts and percentages](https://docs.pushwoosh.com/sms-and-messenger-statistics-1.webp) | Card | What it shows | | :------------- | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Audience** | How many people you targeted for this send, and how many stayed eligible after exclusions. | | **Sent** | Of everyone you targeted, what share the messaging provider accepted for sending. Accepted means the provider took the send into work, not that the message already arrived. | | **Opened** | Of sent recipients, what share opened the message. Only when the platform reports opens. | | **Read (RCS)** | Of recipients whose delivery was confirmed over RCS, what share read the message in the RCS chat. Recipients who got the [SMS fallback](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/#sms-fallback) are left out, because a regular SMS doesn’t report reads. | #### Campaigns distribution **Campaigns distribution** breaks this send into steps: * **Audience:** who you targeted * **Sent:** who was accepted for sending * **Deliveries:** who got a delivery confirmation * **Opened:** who opened (only when the platform reports opens, currently WhatsApp) Otherwise, including for SMS, the funnel stops at **Audience → Sent → Deliveries**. You can view the same breakdown in two ways: as a funnel (default), or as a table. Click **Table view** to open the table. Click **Funnel view** to switch back. Click **Refresh** to reload the numbers. The “Updated at …” line next to it shows when stats last loaded (always UTC). Note The funnel and the cards above always show the full history of this send. The date range on **Timeline** does not change these numbers. ![Campaigns distribution funnel showing Audience, Sent, and Deliveries steps with row breakdowns](https://docs.pushwoosh.com/sms-and-messenger-statistics-2.webp) ##### How to read the funnel * Follow the path down along the steps that appear for this channel. * The big percentage on a step is the share that reached it from the step above. * The list under a step shows how those people split (for example accepted vs rejected). * If a row is missing, nobody fell into that group on this send. * The **Sent** card and the **Sent** funnel step can disagree: the card counts everyone you targeted, the funnel step counts only eligible people. ##### What each step includes * Audience Who stayed eligible, and who was left out (for example frequency capping or a [global control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/)). * Sent Eligible people split into: * **Accepted by gateway:** the provider started processing the send, not that it arrived. * **Rejected by gateway:** a provider-side problem. Details are on the **Errors** tab. * **Processing errors:** a Pushwoosh-side failure. [Learn more](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) * Deliveries Who got a delivery confirmation (not an open). * Usually: confirmed, and not confirmed. * SMS and WhatsApp typically show delivery detail. * Viber and Telegram often do not send delivery confirmations, so empty delivery rows there usually mean “no confirmation data”, not a failed send. For an SMS send with an [RCS template](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/), a note under the step shows how confirmed deliveries reached the phone: * **Delivery confirmed over RCS:** the message arrived as an RCS chat. * **as SMS:** the phone has no RCS, so it got the [SMS fallback](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/#sms-fallback). * **Before the channel was recorded:** deliveries from before Pushwoosh started recording the channel. This part appears only when there are such deliveries. The note appears only when at least one delivery was confirmed over RCS. * Opened Who opened, when this step is shown (currently WhatsApp). How long these numbers stay The main funnel and card numbers stay for up to **one year**. After that, they are removed. Right after you send, delivery and open counts can keep updating for up to **72 hours**. If a very recent send looks wrong, check again later. The numbers usually settle on their own. Data per recipient is kept for a shorter time than the main funnel, usually **30 days**. After that, the send no longer shows: * The RCS and SMS split of confirmed deliveries under **Deliveries**. * The **Read (RCS)** card. A message created automatically by your app or by Pushwoosh, without the Control Panel or the API, does not get a Message stats page. ### Timeline tab Use the **Timeline** tab to see how this send performed hour by hour (or day by day), not only the totals on Overview. For example, you can spot when delivery confirmations came in after you sent, or when rejects piled up. The chart has four lines: * **Accepted by gateway:** the provider took the send into work. * **Rejected by gateway:** the provider rejected the send. * **Delivery confirmations:** the provider or device reported delivery. This is not an open. * **Opens:** someone opened the message (when the platform reports opens). A summary table under the chart totals each metric for the selected window. To configure the chart: 1. Set the **time range**. Times are UTC. For a short range the chart shows points by the hour. For a longer range it groups points by day (or a larger step). 2. Choose **Compare metrics** to plot the four lines together, or **Compare platforms** to compare platforms for one metric. These controls apply to the chart, the summary table, and the **Events** table below. They do not change the Overview cards or funnel. #### Custom events after a WhatsApp message is read For WhatsApp, the **Events** table under the chart shows the custom events your app or website sent after recipients read this message. It fills in only if your app or website sends events to Pushwoosh. Each event goes to the message the user last clicked within the 72 hours before the event: * **What counts as a click:** Reading the message. * **Clicks on other messages:** Clicks on all messages of the same app count, so an event that follows a click on a later message goes to that later message, not to this one. * **Limits:** One event goes to one message only. | Column | What it shows | | :--------- | :------------------------------------------------------------------------------------------------------------------------------------------------ | | **Event** | The event name. | | **Hits** | How many events went to this message. Every event the user sends after reading it counts, until the user clicks another message or 72 hours pass. | | **Unique** | How many distinct users sent the event at least once. | Events are matched to the message by [User ID](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/set-user-id/), not by device. If a phone number is registered under a different User ID than the one your app or website uses for the same person, that person’s events show as 0 here. The time range filters the events, not the reads. A read before the start of the range still counts for an event inside the range. Each row has an export icon that downloads the devices behind that row as CSV, counted by the same rule. Tip If the table is empty, no custom events followed reads of this message in the selected period, or your app or website doesn’t send events to Pushwoosh. You cannot turn event tracking on from Message stats. Ask your development team whether the app or website sends [custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) to Pushwoosh. SMS has no clicks or opens, so an SMS send shows **Events are not available for SMS** instead of the table. Viber and Telegram sends have no **Events** table. ### Errors tab Use the **Errors** tab to see why sends were rejected. See [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) for what the tab shows and [what it doesn’t show](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#what-the-tab-doesnt-show), and [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) for what each error code means. ### Content panel The right-hand **Content** panel shows the message as recipients saw it. WhatsApp uses a chat bubble. SMS and Viber use a simple card preview for now. **Delivery and settings** below the preview shows how this send was configured. For SMS, these values come from when you [created the one-time SMS](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/): * **Status:** whether this send finished (**Done**) or is still processing * **Sent date:** when this message actually went out * **Scheduling:** [immediate](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/#send-immediately) or [selected time](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/#selected-time) * **Target segment:** the [audience](https://docs.pushwoosh.com/product/messaging-channels/sms/send-sms/send-one-time-sms/#send-to-segment) you set * **Target platforms:** the platforms you selected for this channel For WhatsApp setup, see [Send a one-time WhatsApp message](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-one-time-whatsapp/). For Viber, see [Send Viber messages](https://docs.pushwoosh.com/product/messaging-channels/viber/send-viber-messages/). Click **Show all properties** only if you need technical details. [**Request ID**](https://docs.pushwoosh.com/developer/api-reference/api-identifiers/#request-id) is for Support or your developers when tracing this exact send. Journey and some automated sends often have no Request ID. Skip it when you are only checking performance. ### Copy the message code To get an ID for Support or your developers when tracing this send: 1. At the top of the page, click **Message code**. 2. The ID is copied to your clipboard. You usually do not need it to read the stats. ### Export recipients and create a segment #### Export recipients Exporting recipients requires the [**Personal data**](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/#what-the-personal-data-permission-controls) permission in your role. To download recipients as CSV from the top of the page: 1. Click **Export stats**. 2. Confirm the download. You can also click **Export list** from: * A card’s three-dot menu (⋮) on **Overview** * An error row on the **Errors** tab Both **Export stats** and **Export list** pull from the same per-recipient log, which covers only the last **30 days**. Older recipients are not in the file. The confirmation dialog states this limit before the download starts. #### Create a segment from Sent Use this when you want to message the same people again, for example everyone the provider accepted for sending. 1. On **Overview**, find the **Sent** card. 2. Click the three-dot menu (⋮) on that card. 3. Click **Create segment**. 4. In the **Create segment** dialog, enter a name in **Segment name**. 5. Click **Create**. The new [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) includes the people on that card. ## Additional resources * [SMS and messenger statistics FAQ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/messaging-statistics-faq/#sms-and-messengers) * [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) * [Sending errors](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) * [Message history](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history/) --- # Web campaign statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/web-campaign-statistics/ See visits, impressions, clicks, and form submits of a web campaign, see why submissions failed, and export subscribers. --- The statistics page of a web campaign shows how visitors moved from loading the page to seeing the campaign and, for a [web form](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/), to submitting it. For a form with an email field, you can also export everyone who subscribed through the campaign. ## View web campaign statistics 1. Go to **Campaigns → Web campaigns**. 2. Click the campaign name. You can also open the three-dot menu on its row and choose **View statistics**. To copy the campaign code, click **Campaign code** at the top of the page. To change the campaign, click **Campaign settings**. ## Read the funnel The **Overview** tab shows the campaign’s numbers for its whole lifetime. Pushwoosh counts every occurrence, not unique visitors, so a visitor who returns or submits twice is counted twice. ![Web campaign statistics with Visits, Impressions, and Submits cards, the funnel, and failed submissions by reason](https://docs.pushwoosh.com/messaging-channels-web-popups-web-campaign-statistics-1.webp) Each card and funnel step shows its share of the previous step, for example **Submits** as a percentage of **Impressions**. To see the numbers as a table, open the **Funnel view** menu and choose **Table view**. Click **Refresh** to load the latest numbers. ### What each step counts The table says what every card and funnel step counts. | Step | What it counts | | ---------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Visits** | The page loads where the campaign was ready to show. | | **Impressions** | The times visitors actually saw the campaign. | | **Submits** | The times visitors submitted the form. Shown only for campaigns with a web form. | | **Confirmed** | The times visitors confirmed their email by the link in the [double opt-in](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/#confirm-emails-with-double-opt-in) email. Shown only for a web form while double opt-in is on in the project. | | **Interactions** | The times visitors clicked links and buttons in the content. A card without a percentage, not a funnel step, shown for every campaign. | A campaign with regular web content shows **Visits**, **Impressions**, and **Interactions**. **Confirmed** follows the current double opt-in setting, so turning double opt-in off hides it for earlier confirmations too. #### What counts as an interaction * A click on any link or button inside the content counts, including choice and rating buttons. * A click on the ✕ that closes the campaign doesn’t count. * The **Submit** button of a web form doesn’t count on the **Interactions** card. Submissions have their own **Submits** step. The **Interactions** column in the [campaign list](https://docs.pushwoosh.com/product/messaging-channels/web-popups/#monitor-your-campaigns) adds submissions to clicks, so for a form it can be higher than the card. * Every click counts, so a visitor who clicks twice is counted twice. * Clicks are recorded only on sites with [Pushwoosh WebSDK](https://docs.pushwoosh.com/developer/pushwoosh-sdk/web-push-notifications/web-push-sdk-30/) 3.103.0 or later. Clicks made before that aren’t counted, so an older campaign can show 0 for that time. #### What counts as a visit * A Popup counts a visit once the visitor matches its audience, device, pages, and frequency settings, before the display delay runs out. * An Inline campaign counts a visit once its block is placed in the page element. * A page load where the frequency setting holds the popup back, or where no element matches, isn’t a visit. * A campaign set to **Only via WebSDK call** counts a visit when your site shows it. #### Why impressions are lower than visits A Popup counts an impression when it’s shown, and an Inline campaign when it scrolls into view. A visitor can leave the page before the delay runs out or without scrolling to the block, so **Impressions** is usually lower than **Visits**. ### Failed submissions Under **Submits**, **Failed submissions** lists the submissions Pushwoosh rejected, grouped by reason and sorted by count: | Reason | What it means | | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Invalid or disposable email** | The address is malformed or uses a disposable email domain. The visitor sees the field’s invalid email text. | | **Email bounced earlier** | An email to this address bounced before. The visitor sees the field’s invalid email text. | | **Field values rejected** | The submitted values don’t match the form as it’s saved now: the request has a field the form no longer has, a required field is empty, or the consent checkbox isn’t selected. The Web SDK checks the fields before it sends them, so this usually means the form was edited while visitors still had the earlier version open, or the request didn’t come from the Web SDK. A few such failures right after you edit a live form are expected. | | **No verified sender domain** | Double opt-in is on, but your account has no verified sending domain, so the confirmation email can’t be sent. To fix it, [verify a sending domain](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/#before-you-start-verify-a-sending-domain). | | **Not included in the plan** | Your plan doesn’t include email, so the address couldn’t be subscribed. With double opt-in on, this check happens when the visitor clicks the confirmation link, and such failures aren’t counted here. | | **Server error** | Pushwoosh couldn’t process the submission. | | **Unknown reason** | The reason wasn’t recorded. | For the two invalid email reasons, the visitor sees the field’s invalid email text. For all other reasons, the visitor sees the **Submit error text** and can submit again. Submissions that never reach Pushwoosh, for example because the visitor went offline, aren’t counted. ## Export subscribers **Export subscribers** appears for a campaign whose web form has an **Email input**, if you have access to personal data. 1. Click **Export subscribers**. ![Export subscribers dialog with Status set to All and a Submission date range](https://docs.pushwoosh.com/messaging-channels-web-popups-web-campaign-statistics-2.webp) 2. In **Status**, choose **All**, **Confirmed**, **Pending**, or **Expired**. **Pending** addresses haven’t confirmed double opt-in yet. **Expired** addresses didn’t confirm within 24 hours. 3. In **Submission date**, set the period. By default it’s the last 30 days. The period applies to the date of each address’s latest submission. 4. Click **Export**. A banner under the page header shows the export progress and then the file. Keep the page open until the file is ready. The banner also shows the latest export of this campaign when you come back to the page. ### What the file contains The CSV file has one row per email address submitted in the campaign, with these columns: * `email` * One column per tag that appears in the exported rows, in alphabetical order. A cell holds the value from the address’s latest submission and is empty if that submission left the field empty. * `status`: `confirmed`, `pending`, or `expired`. It’s empty for an address whose email bounced earlier. * `submitted`: the date of the latest submission. * `confirmed`: the date the address was confirmed. Without double opt-in, an address is confirmed as soon as it’s submitted, so this is the date of its first submission. A visitor who submits the form in two campaigns appears in the export of each campaign. ## Additional resources * [Web forms](https://docs.pushwoosh.com/product/content/web-popup-content/web-forms/) * [Confirm subscribers with double opt-in](https://docs.pushwoosh.com/product/audience-data-and-segmentation/subscription-forms/double-opt-in/) * [Create Web popup campaigns](https://docs.pushwoosh.com/product/messaging-channels/web-popups/create-web-popups-campaigns/) --- # Metrics glossary **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/ This glossary defines the metrics used to track the performance of your Pushwoosh campaigns and explains key analytics terms. --- ## Pushwoosh Customer Journey ### Audience #### Campaign reach Number of users who entered the journey (counted as non-unique users’ entries). #### Unique users Number of unique users who entered the journey. #### Inside the Journey Number of users currently progressing through the journey (i.e., those who have not reached the Exit element yet) #### Drop-offs Number of users removed from the journey due to various issues, such as being unregistered from the app or not meeting Time Delay conditions based on user tags or behavior. [Learn more](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey) ### Messages #### Total sent Total number of messages sent within the journey. #### Average CTR Average click-through rate for all messages on any channel sent within the journey. ## Push (Mobile/Web) ### Push sends Number of push notifications sent, including to the users that have opted out from push notifications ### Push opens Number of push notifications that were opened by recipients. ### Push deliveries Number of mobile push notifications that have been successfully transmitted by the Pushwoosh platform to intended recipients’ mobile devices. It means that the notification was sent from the sender’s side and reached the servers or gateways of the recipients’ devices. However, it does not necessarily guarantee that a user has seen or interacted with the notification. ### Unshowable sends Number of push notifications that the platform provider accepted for a device with notifications turned off in the OS settings. No banner is shown, but the message still reaches the app, and it appears in [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) if the send has [Save message to Inbox](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/#save-message-to-inbox) enabled. Pushwoosh counts these sends as successful, not as errors. In the [recipients export](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics#what-the-file-contains) they are marked `Unshowable`. ### Push CTR Percentage of users who opened the push after receiving it. **Metric calculation:** CTR (%) = (Opened / Recipients) × 100 **Opened** the number of users who opened the push. **Recipients** is the number of users who have your app installed, have a valid push token in Pushwoosh, and allowed push notifications for the app. ### Recipients Users who have your app installed, have a valid push token in Pushwoosh, and have allowed push notifications for the app. ### Open-to-sent rate Opens divided by sends for the period. Used on both the Push and Email tabs of the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/). It’s not the same denominator as [Push CTR](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#push-ctr), which divides by recipients. ### Delivery rate Deliveries divided by sends for the period, shown as a tile on the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Push tab. The tab’s own daily chart computes delivery-adjacent rates differently by metric, check the page for the exact split. ## Email ### Email sends Number of emails sent to recipients. ### Emails opened unique The number and percentage of unique users who opened an email. Each user is counted once regardless of how many times they opened it. ### Excluded AMPP Open rate for **Emails opened unique** with Apple Mail Privacy Protection (AMPP) opens removed. AMPP can pre-load tracking pixels in Apple Mail and iCloud and inflate the main open rate. Shown alongside the main rate for comparison. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics#what-each-step-includes) ### Emails opened total The total number of email opens, including repeat opens by the same user. If a user opens the email multiple times, each open is counted. ### Emails delivered Number of emails delivered to recipients. Email delivery may fail because of bounces, so we recommend ensuring that your mailing list contains only valid and verified email addresses from users who explicitly gave their consent to receive emails from you. ### Email CTR Percentage of recipients who clicked at least one link in the email. **Metric calculation:** CTR (%) = (Clicked / Emails sent) × 100 **Clicked** is the number of users who clicked at least one link in the email. **Emails sent** is the number of emails sent to recipients. ### Unique click email links In-email link clicked by a user at least once. Even if a user clicks on a link more than once, it still counts as one unique click. ### Emails hard bounce Number of emails not delivered because of hard bounces. A hard bounce happens when an email message can’t be delivered permanently because the recipient’s address either doesn’t exist or is invalid. ### Emails soft bounce Temporary delivery failures, such as when a recipient’s mailbox is full or network issues disrupt delivery. Pushwoosh makes multiple attempts to resend soft-bounced emails. If the email remains undelivered after these attempts, sending to that email address will be discontinued. ### Email rejects Instances where the recipient’s email server declines to accept an email during the SMTP (Simple Mail Transfer Protocol) transaction. Rejections can result from reasons like authentication failure, spam filtering, or policy-based restrictions. ### Email complaints Number of users who have reported your message as spam, informed their email service provider of their complaint regarding your email, or expressed dissatisfaction in a manner recognized by email providers and clients as a reason for potentially limiting your ability to send messages. ### Email unsubscribed Number of recipients followed the Unsubscribe link in that email. Such users are tagged with the True value of the Unsubscribed Emails default tag. ### Click-to-delivery rate Clicks divided by deliveries for the period. Shown on the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Email tab only. ### Click-to-open rate Clicks divided by opens for the period. Shown on the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Email tab only. ### Complaint rate Complaints divided by sends for the period, on the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Email tab Audience section. ### Hard bounce rate Hard bounces divided by sends for the period, on the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Email tab Audience section. ### Subscribers activity distribution On the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Email tab, every subscriber sorted into **Clicked**, **Opened but not clicked**, or **Dormant** (no open in the period) for the selected period. A subscriber whose only opens were automated Apple Mail Privacy Protection prefetches, not a real read, still counts as Dormant. ## In-Apps ### In-App impressions Number of times the in-app was displayed to users during the selected period. ### In-App interactions Number of times users engaged with the in-app message, such as button clicks or link taps. ### Impression duration Total amount of time a user spent viewing your in-app message. ### Skipped Number of users who closed the in-app without interacting with it. ## Frequency capping ### Suppressions Number of messages that were not sent because the recipient had already reached their frequency capping limit. ### Affected users Number of unique users who had at least one message suppressed by frequency capping. ### Total suppressions On the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/)’s Push and Email tabs, the frequency capping [Suppressions](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#suppressions) count for the selected period, switchable to count by [Affected users](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#affected-users) instead. ## Audience ### App installs Number of application installs, i.e. the number of times a mobile software application was downloaded, installed, and prepared for use by end-users within a specified time frame. ### App uninstalls Number of devices for which push tokens were removed from the database due to their invalid or non-existent status, typically occurring when users have uninstalled the application. ### App opens Number of times the app has been launched on users’ devices as of the date and time specified. ### Daily active users (DAU) Number of unique users that opened the app on a certain day. ### Monthly active users (MAU) Number of unique users that opened the app over 30 days. [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) show one MAU value per month, counted over the last 30 days of that month. For example, MAU for September counts users who opened the app from September 1 to September 30. In a 31-day month, the window starts on the 2nd, so MAU for October covers October 2 to 31. For the current month, the 30 days end yesterday. Billing counts MAU differently on each plan: * **Push only:** users who opened the app from a device with a valid push token in the 30 days before the end of the billing period. * **Omnichannel:** users who opened the app from a device with a valid push token, plus users who received an email, SMS, or WhatsApp message, in the 30 days before the end of the billing period. A user who only gets your emails or SMS still counts, even if they never open the app. On both plans, each user counts once per project, however many channels reach them. A user active in two projects counts in each of them. To see your current billed MAU, open **Usage** on the [Billing page](https://docs.pushwoosh.com/product/account-management-and-security/billing-page/#overview). ### Daily active devices (DAD) Number of unique devices that opened the app on a certain day. ### Monthly active devices (MAD) Number of unique devices that opened the app during a specific month. ### Recipients Users you can reach with communication outside your app: push notifications or email. ### Subscribers Number of devices with push tokens or emails provided, meaning they are reachable with push notifications, emails, or in-apps. However, even if there is a push token associated with a device, some users may not see push notifications in case they disabled alerts in their device settings. Those users are reachable with Silent (background) pushes, in-app messages, and can receive notifications to the inbox. ### Push enables The number of opt-ins for push notifications. Counted as non-unique, i.e. the number will include each instance of a user opting in, whether it’s the first time or a re-opt-in for push notifications. ### Push disables The number of opt-outs from push notifications. Counted as non-unique, i.e. the number will include each instance of a user opting out. ### Total users Every user with at least one push device registered, with or without a valid token. On the [Overview page](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/), this is the starting point of the [Push tab’s Funnel](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#funnel). ## Devices ### Total devices All devices registered on the Pushwoosh platform and active within the last 90 days. They may or may not have push tokens associated with them. ### Subscribed devices Number of devices with push tokens or emails provided, meaning they are reachable via at least one channel: push notifications, emails, or in-app messages. ### Unsubscribed devices Devices that have opted out or unsubscribed from one or more communication channels, such as push notifications, emails, or in-app messaging. ### Push enabled devices Devices with a valid push token that can receive notifications. On mobile devices, users can ban the display (rendering) of push notifications on the lock screen in the app-specific OS settings. However, such devices are still considered push-enabled because the token is preserved, and they can receive silent notifications (sent to the app inbox, containing custom data, etc.) Devices become push-disabled when their push token is revoked or invalidated, usually by uninstalling the app or disabling push notifications in the device’s system settings. ## Events Any user activity within the app, website, or backend system. They can be used in segmentation and as triggers for behavior-based messaging. --- # Overview page **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/ Track users, retention, push and email performance, and which campaigns drive conversions and revenue on the Overview page. --- The **Overview** page shows audience, push, email, and conversion trends without setting up a dashboard or report. Check it first to catch a drop in retention or a spike in delivery errors early, rather than a week into a bad trend. Open **Project overview** in the left menu. * [**App metrics**](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#app-metrics-tab): check active users (MAU/DAU), platform and channel reach, and Day 1 / 7 / 14 / 28 retention * [**Push**](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-tab): check push audience, delivery performance, and frequency capping impact * [**Email**](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#email-tab): check subscriber base, delivery performance, category and mailbox-provider breakdowns, and frequency capping impact * [**Conversions**](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#conversions-tab): check which campaigns, journeys, and in-apps lead to conversions and how much revenue they bring Note The **Push** tab is disabled until at least one mobile or web push platform is configured for the project, and the **Email** tab is disabled until email is configured. **App metrics** stays available either way. Get statistics by email To get message statistics from this page by email on a schedule, set up [auto-reports](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/auto-reports/). ## Set the reporting period Use the date range picker to choose which period the **Overview** page reports on. Every chart on the **App metrics**, **Push**, **Email**, and **Conversions** tabs uses the same range, so you compare metrics for the same window. By default, the page shows the last 30 days. To change the period: 1. Click the **date range** picker in the top-right corner, next to **Project settings** and **Export statistics**. 2. Choose **Last 7 days**, **Last 30 days**, or set a custom start and end date on the calendar. ![Date range picker on the Overview page with Last 7 days and Last 30 days presets, dual-month calendar, and Reset and Apply buttons](https://docs.pushwoosh.com/overview-page-date-range-1.webp) 3. Click **Apply**. ## App metrics tab Check how many users are active in your app and how well you retain them, so you can spot growth or churn early without leaving the project overview. ### Unique users Check the current value and the change compared to the previous month or day: * **MAU**: monthly active users, over a rolling 30-day window ending on each plotted day * [**DAU**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#daily-active-users-dau): daily active users, unique users who opened the app that day Caution **MAU** here doesn’t change with the date range you pick. [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) show one **MAU** value per month instead, and [the way billing counts MAU depends on your plan](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#monthly-active-users-mau), so don’t compare the numbers directly. ![Overview page App metrics tab showing the Unique users MAU and DAU charts, with the App metrics, Push, and Email tabs above](https://docs.pushwoosh.com/overview-page-app-metrics-1.webp) ### User distribution by platforms Check how many users are reachable on each channel configured for the project (individual mobile and web push platforms, email, SMS, and chat channels such as WhatsApp, Telegram, and Viber), as of the last day of the selected period. ![User distribution by platforms card showing a stacked bar and legend broken down by platform](https://docs.pushwoosh.com/overview-page-app-metrics-2.webp) Treat it as a snapshot, not a sum over the period: the platforms can add up to slightly more than the project total, because every platform a user is reachable on counts them once. ### Channel reach Check how many users you can reach on at least one channel, how many on two or more, and which exact combination of channels each user falls into, as of the last day of the selected period. ![Channel reach card on App metrics showing Total contacts, Reachable in at least 1 channel, Reachable in at least 2 channels, rows of channel combinations, and Unreachable](https://docs.pushwoosh.com/overview-page-app-metrics-4.webp) What each part of the card means: * **Total contacts**: every User ID in the project, including users with no device, such as users added by a tag import. Users with no device count as **Unreachable**. * **Reachable in ≥ 1 channel**: users you can reach on at least one channel, and their share of **Total contacts** * **Reachable in ≥ 2 channels**: users you can reach on two or more channels, and their share of **Total contacts** * **Channel combination rows**: each row is one exact set of channels. A user in the **Push** + **Email** row is reachable on push and email and on no other channel. A row marked **only** has a single channel. * **Unreachable**: users you can’t reach on any channel A user counts as reachable on a channel when: * **Push**: their device has a [push token](https://docs.pushwoosh.com/product/first-steps/pushwoosh-glossary/#push-token-also-known-as-device-push-token) and push permission. All push platforms, including web push, count as one channel. * **Email**: their address hasn’t unsubscribed and hasn’t hard-bounced * **SMS**: they have a phone number registered for SMS * **WhatsApp**: they have a phone number registered for WhatsApp * **Inbox**: they have any app or browser device, with or without a push token. Inbox appears only if the project used [Message Inbox](https://docs.pushwoosh.com/product/messaging-channels/message-inbox/) in the last 30 days. Unlike [User distribution by platforms](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#user-distribution-by-platforms), each user is counted once here, so the rows and **Unreachable** add up to **Total contacts**. A person stored under two User IDs is counted twice. A row’s count can differ from the size of a segment you build with the same conditions. The card shows a snapshot of the last day of the selected period, while a segment’s size reflects the data at the moment you last calculated it. A segment also shows devices per channel, not users in an exact combination of channels. How to use Channel reach * Compare **Reachable in ≥ 1 channel** with **Total contacts** to see what share of your users you can reach at all. * Use **Reachable in ≥ 2 channels** to estimate how many users you can follow up with on a second channel. * Check the largest single-channel rows before you pick a channel for a campaign. A large **Push** only row means an email-only campaign misses those users. * Target the single-channel rows to grow reach. For example, if a large share of users is reachable only by email, send those users a campaign that asks them to subscribe to push. ### User retention Check **Day 1**, **Day 7**, **Day 14**, and **Day 28** retention to see how many new users return after their first visit, and whether they come back in the first week and stay engaged over the following weeks. Each day counts only users who returned on exactly that day, not everyone who returned by then. The cohort is everyone who first visited on one specific day, 30 days before the last day with data, not everyone who first visited during your selected period. Changing the date range doesn’t change which cohort is shown. ![User retention card on App metrics showing Day 1, Day 7, Day 14, and Day 28 return rates for the cohort](https://docs.pushwoosh.com/overview-page-app-metrics-3.webp) Note The first visit is the [`PW_DeviceRegistered`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered) event, which fires when a device first gets a push token. In iOS and Android apps, that includes users who don’t allow notifications. Users whose device never gets a push token aren’t in the cohort. A return is an app launch that the Pushwoosh SDK reports with [applicationOpen](https://docs.pushwoosh.com/developer/api-reference/device-api/#applicationopen). You cannot change how the Overview page counts first visits and returns. To measure retention for your own events, use [User retention statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics/). ## Push tab Use this tab to check who you can reach with push, how many pushes got delivered and opened over the selected period, and how frequency capping affected sends. ### Audience Check your push audience as of the last day of the period and how it changed since. When more than one push platform is configured for the project, use the **All platforms** dropdown next to the section heading to filter Funnel, Subscribers, [Push reachable](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-reachable), and the [Frequency capping](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#frequency-capping) section below by a single platform. Base change and the Performance section aren’t affected by this dropdown. #### Funnel Check the steps from **Total users** down to **Active-reachable**. Each step shows how many users remain and what share that is of the step before it. The funnel is a snapshot for the last day of the selected period. It does not add up activity across the whole period. ![Push Audience Funnel card showing Total users, Registered users, Push reachable, and Active-reachable with shares as of the last day of the period](https://docs.pushwoosh.com/overview-page-push-audience-1.webp) * [**Total users**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#total-users) * [**Registered users**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#subscribers): has a [push token](https://docs.pushwoosh.com/product/first-steps/pushwoosh-glossary/#push-token-also-known-as-device-push-token) * [**Push reachable**](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-reachable): users who can actually receive a push * **Active-reachable**: [Push reachable](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-reachable) and opened the app in the last 30 days. This window is fixed and doesn’t change with the date range you pick. #### Subscribers Check which segment each user falls into: ![Push Audience Subscribers card with Reachable and active, Reachable and dormant, Permission off, Never opted-in, and Ask AI Assistant](https://docs.pushwoosh.com/overview-page-push-audience-2.webp) * **Reachable and active**: [Push reachable](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-reachable) and opened the app in the last 30 days * **Reachable and dormant**: [Push reachable](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#push-reachable) but hasn’t opened the app in 30+ days * **Permission off**: [push token](https://docs.pushwoosh.com/product/first-steps/pushwoosh-glossary/#push-token-also-known-as-device-push-token) present, system permission revoked * **Never opted-in**: no push token, never subscribed to push On a row, open the kebab menu and choose **Create segment** or **Export CSV**. ![Push Audience Subscribers card with the kebab menu open on a row, showing Create segment and Export CSV](https://docs.pushwoosh.com/overview-page-push-audience-2b.webp) To ask about this audience in chat, click **Ask AI Assistant** in the card header when the AI Assistant is enabled for the account. ![Push Audience Subscribers card header with the Ask AI Assistant button](https://docs.pushwoosh.com/overview-page-push-audience-2c.webp) AI Assistant opens in a panel on the same page with a ready prompt about this audience. Edit the prompt if you need to, then send it. ![AI Assistant panel open on the right of the Push tab, with a new chat and a prompt about the push audience in the message box](https://docs.pushwoosh.com/overview-page-push-audience-2d.webp) #### Push reachable **Push reachable**: users who can actually receive a push. Pushwoosh has a valid push token for their device, and they haven’t turned off notifications in their phone or browser settings. Check how many users could receive a push each day of the period. A drop usually means people turned off notifications or uninstalled the app. A rise means new opt-ins. ![Push Audience Push reachable card showing the Push reachable count trend over the selected period](https://docs.pushwoosh.com/overview-page-push-audience-3.webp) #### Base change Check how many subscribers left the base through auto-cleanup and expired tokens, and how many joined it through new registrations, since the start of the period. Each row’s bar is sized against the base at the start of the period: ![Push Audience Base change card showing auto-cleanup, expired tokens, and new registrations against the base at the start of the period](https://docs.pushwoosh.com/overview-page-push-audience-4.webp) * **Auto-cleanup:** removes a device’s [push token](https://docs.pushwoosh.com/product/first-steps/pushwoosh-glossary/#push-token-also-known-as-device-push-token) once the app has gone unopened past the account’s inactivity limit. See [automatic inactivity cleanup](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/manage-push-subscriber-base/#automatically-remove-inactive-tokens-after-90-days-with-autovalidation). * **Expired tokens:** a device whose push token APNs or FCM reported invalid, based on gateway responses only. See [invalid push tokens](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/manage-push-subscriber-base/#how-to-keep-your-subscriber-list-clean-by-removing-invalid-push-tokens). * **New registrations:** devices that registered for push during the period Pushwoosh only keeps this breakdown for the last 60 days. If your period starts earlier, the line under the breakdown shows the date it’s actually counted from. ### Performance Check push delivery and engagement for the period, then open the daily breakdown by platform. ![Push tab Performance section showing the By sends dropdown and the Sent, Delivery rate, Open-to-sent rate, and Errors tiles](https://docs.pushwoosh.com/overview-page-push-performance-1.webp) Switch the dropdown above **Sent**, **Delivery rate**, **Open-to-sent rate**, and **Errors**: * **By sends:** every send counted separately * **By uniques:** each recipient counted once. In this mode, the first metric becomes **Recipients**: unique devices the push was delivered to during the period, each counted once even if they received several sends. Check these summary metrics for the period: * **Sent:** how many pushes went out (becomes **Recipients** in **By uniques** mode) * [**Delivery rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#delivery-rate): how deliveries compare to sends (includes the delivery count) * [**Open-to-sent rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#open-to-sent-rate): how opens compare to sends (includes the open count) * **Errors:** how many sends failed #### Daily performance Check these metrics per day, split by platform. Switch metrics with the chips above the chart: **Sent**, **Deliveries**, **Delivery rate**, **Opens**, **Open rate**, and **Errors**. ![Push tab Daily performance chart with metric chips and an iOS/Android legend over the selected period](https://docs.pushwoosh.com/overview-page-push-performance-2.webp) ### Frequency capping Check how [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) affected sends over the period. To check one push platform, pick it in the **All platforms** dropdown in the [Audience](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#audience) section. ![Push tab Frequency capping section showing total suppressions and a bar chart over the period](https://docs.pushwoosh.com/overview-page-push-frequency-capping-1.webp) The chart shows how many pushes frequency capping blocked, or how many users it affected, per day for the period. The [**Total suppressions**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#total-suppressions) figure in the header sums the same for the whole period. Switch what both count with the selector: * [**By suppressions**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#suppressions): how many times a message was blocked by frequency capping * [**By users**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#affected-users): how many unique users were affected ## Email tab Use this tab to check subscriber-base health, engagement, category and mailbox-provider breakdowns, and frequency capping impact over the selected period. Caution If your complaint rate or hard bounce rate exceeds your plan’s limit, Pushwoosh automatically pauses email sending for the project. A banner with a **Contact support** button appears at the top of this tab, and the **Email** tab itself shows a warning icon. By default, opens and clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners) aren’t counted on this tab. To count them, turn on **Show bots** at the top of the tab. The switch changes **Subscribers activity distribution**, **Event-triggered performance**, and **Category-based performance**. The switch isn’t saved, so it’s off again the next time you open the page. ### Audience Check your subscriber base, what changed it, and how engaged subscribers are. #### Subscribers base Check the subscriber count trend for the period. ![Email Audience Subscribers base card showing the subscriber count trend for the selected period](https://docs.pushwoosh.com/overview-page-email-audience-1.webp) #### Base change Check how many subscribers the base lost through complaints or hard bounces, and how many it gained through new subscribers, since the start of the period. Unsubscribes and other changes are tracked too, but don’t remove the address on their own. Each row’s bar is sized against the base at the start of the period: ![Email Audience Base change card showing unsubscribes, complaints and hard bounces, new subscribers, and other changes](https://docs.pushwoosh.com/overview-page-email-audience-2.webp) * **Unsubscribe:** subscribers who [unsubscribed](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#email-unsubscribed) in the period. The address stays on the base, so this never reduces it. * **Complaints and hard bounces:** subscribers Pushwoosh added to the [suppression list](https://docs.pushwoosh.com/product/messaging-channels/emails/email-suppression-list/) for a spam complaint or a hard bounce, counted once even if both happened * **New subscribers:** devices or addresses that subscribed to email during the period * **Other changes:** everything the two flows above don’t cover: daily snapshot offset, a second address on an existing user, transfers between accounts and migrations, deleted devices and users, and base cleanups. Can be positive or negative. #### Complaint rate and Hard bounce rate Check the [**Complaint rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#complaint-rate) and [**Hard bounce rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#hard-bounce-rate) over the period and the current value in the header. ![Email Audience card with Complaint rate and Hard bounce rate charts for the selected period](https://docs.pushwoosh.com/overview-page-email-audience-3.webp) #### Subscribers activity distribution Check how the [**Subscribers activity distribution**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#subscribers-activity-distribution) splits into these groups, with each count and share of the base: ![Email Audience Subscribers activity distribution with Clicked, Opened but not clicked, Dormant, and Re-engage with AI](https://docs.pushwoosh.com/overview-page-email-audience-4.webp) * **Clicked** * **Opened but not clicked** * **Dormant:** no open in the period On a row, open the kebab menu and choose **Create segment** or **Export CSV**. ![Email Audience Subscribers activity distribution with the kebab menu open on the Clicked row, showing Create segment and Export CSV](https://docs.pushwoosh.com/overview-page-email-audience-4b.webp) On the **Dormant** row, click **Re-engage with AI** when the AI Assistant is enabled for the account. AI Assistant opens in a panel on the same page with a ready prompt about your dormant subscribers. ![Email Audience Subscribers activity distribution Dormant row with the Re-engage with AI button](https://docs.pushwoosh.com/overview-page-email-audience-4c.webp) Note Pushwoosh automatically counts a subscriber whose only opens were automated Apple Mail Privacy Protection prefetches, not a real read, as **Dormant**. If that applies to at least one subscriber, read the line below the distribution for how many. ### Event-triggered performance Check email delivery and engagement totals for the period, then open the daily breakdown. ![Email tab Event-triggered performance section showing Sent, Open-to-sent rate, Click-to-delivery rate, and Click-to-open rate tiles with a daily chart](https://docs.pushwoosh.com/overview-page-email-performance-1.webp) Switch the dropdown above **Sent**, **Open-to-sent rate**, **Click-to-delivery rate**, and **Click-to-open rate**: * **By sends:** every send counted separately * **By uniques:** each recipient counted once Check these summary metrics for the period: * **Sent:** how many emails went out (volume for the period) * [**Open-to-sent rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#open-to-sent-rate): how opens compare to sends, whether the send got opened * [**Click-to-delivery rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#click-to-delivery-rate): how clicks compare to deliveries, whether delivered mail drove clicks * [**Click-to-open rate**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#click-to-open-rate): how clicks compare to opens, whether openers engaged further #### Daily chart Click a chip above the chart to add or remove a line: * On by default: **Sends**, **Opens**, **Unsubscribes** * Start off: **Deliveries**, **Hard bounces**, **Complaints** ### Category-based performance Check send performance by [email category](https://docs.pushwoosh.com/product/messaging-channels/emails/preference-center/set-up-preference-center/). ![Email tab Category-based performance table with Name, Total send, Clicked, Click-to-open rate, and Category unsubscribe columns](https://docs.pushwoosh.com/overview-page-email-category-1.webp) When the table is available: * Sends with no category attached are grouped under **Uncategorized**. * Click a column header to sort. If no categories are set up yet, this section shows a card to set them up instead of the table. ### Mailbox providers Check send performance by the recipient’s mailbox provider (Gmail, Outlook, and so on). ![Email tab Mailbox providers table showing Provider, Sent, Opens, Delivered, Hard bounce, Complaints, and Unsubscribes columns](https://docs.pushwoosh.com/overview-page-email-providers-1.webp) Providers Pushwoosh can’t attribute a send to are grouped under **Other**. Click a column header to sort. ### How Gmail sees your domain The **How Gmail sees your domain** section shows how Gmail rates mail from your sender domain: the spam rate reported by Gmail users and the share of mail Gmail refused, with the main reason and what to do about it. The data comes from Google Postmaster Tools. The section shows data once at least one sender domain of your account is [connected to Gmail Postmaster](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#connect-your-domain-to-gmail-postmaster) and verified. If several domains are verified, choose one in the domain selector next to the section title. ![Email tab How Gmail sees your domain section with an at-risk spam rate, a daily spam rate chart, and Gmail rejects with the main reason](https://docs.pushwoosh.com/overview-page-email-postmaster-1.webp) Keep in mind how Gmail reports this data: * **It covers all mail from the domain.** Mail you send from the same domain through other services is included, not only mail sent via Pushwoosh. * **It needs enough volume.** Gmail reports a day only for domains that send it enough mail. If the domain sent too little mail to Gmail in the selected period, the section says there isn’t enough mail for Gmail to report. * **It arrives about 3 days late.** The newest days of the period stay empty, and the chart title marks them with “no data from Gmail yet”. #### Before a domain is connected If none of your sender domains is connected to Gmail Postmaster and one of them has verified DKIM records, the section shows an invitation card instead of data. Click **Connect Gmail Postmaster** on the card to open that domain’s **Gmail Postmaster** block in the email channel settings, then follow the [connection steps](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#connect-a-domain) from there. ![Email tab How Gmail sees your domain invitation card with the Connect Gmail Postmaster button.](https://docs.pushwoosh.com/overview-page-email-postmaster-2.webp) While a domain is waiting for Google to confirm its TXT record, the section isn’t shown. Once Google verifies the domain, the section shows its data. #### Spam rate The spam rate is the share of mail that Gmail users marked as spam, averaged over the days Gmail reported in the selected period. Next to it, you see the change from the previous period of the same length, in percentage points. Compare it with Gmail’s thresholds: * **Under 0.1%:** the rate is healthy. Keep it there. * **0.1% or higher:** the value turns orange and an **At risk** badge appears. * **0.3% or higher:** the value turns red. At this rate, Gmail starts rejecting mail. The **Spam rate by day** chart shows the daily values with both thresholds as dashed lines, so you can see which days pushed the rate up. #### Mail Gmail refused If Gmail refused less than 0.5% of the domain’s mail in the selected period, the section says **Gmail accepts your mail normally**. Otherwise, it shows the share of refused mail, the main reason with its share, and the steps that address it. Other reasons are listed under **Also**. The share includes both mail Gmail rejected and mail it temporarily refused. The badge shows who acts on the main reason. **Action needed** means the fix is on your side. **On our side** means the reason concerns Pushwoosh sending infrastructure, and you don’t need to do anything. The following table lists the reasons and what to do about each: | Reason | What it means | What to do | | --------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | | **Low domain reputation** | Gmail doesn’t trust your domain enough, usually after spam complaints or sending to inactive addresses. | Stop sending to subscribers inactive for 90 days or more, bring the spam rate under 0.1%, and don’t raise volume sharply while reputation recovers. | | **Suspected spam content** | Gmail considers the content of your emails likely spam. | Review your template content and wording, check that links point to trusted domains, and avoid spam-like subject lines. | | **Sudden volume spike** | Gmail saw an unusual jump in how much mail you send. | Raise volume gradually over several days and spread large campaigns over time instead of sending them in one burst. | | **Low reputation of our sending IP**, **our sending IP is on a blocklist**, **missing reverse DNS of our sending IP** | The reason concerns Pushwoosh sending IPs. | Nothing on your side. Pushwoosh handles it. | | **Unsupported attachment**, **Gmail policy error**, **other reasons** | Gmail refused part of your mail for this reason. | Make sure your mail follows Gmail’s sender guidelines. | To lower your spam rate and keep your list healthy, see [Email deliverability best practices](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/). ### Frequency capping Check how [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) affected email sends over the period. ![Email tab Frequency capping section showing total suppressions and an Unsubscribe rate vs Sending frequency chart](https://docs.pushwoosh.com/overview-page-email-frequency-capping-1.webp) #### Total suppressions The chart shows how many emails frequency capping blocked, or how many subscribers it affected, per day for the period. The [**Total suppressions**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#total-suppressions) figure in the header sums the same for the whole period. Switch what both count with the selector: * [**By suppressions**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#suppressions): how many times a message was blocked by frequency capping * [**By users**](https://docs.pushwoosh.com/product/statistics-and-analytics/metrics-glossary/#affected-users): how many unique users were affected A suppression tied to a message sent more than 30 days before the start of the period can’t be attributed to a channel, so it’s excluded here and counted on the Push tab’s Total suppressions instead. If that happened, read the line below the chart for how many. #### Unsubscribe rate vs Sending frequency Use this chart to see whether people who get more emails in the period also unsubscribe more often. This is a correlation, not a cause: an unsubscribe isn’t necessarily triggered by those specific emails. Subscribers are grouped by how many emails they received in the period. Each group shows its unsubscribe rate. Subscribers who unsubscribed but received no email in the period are left out of every group. If any were left out, a line below the chart shows how many. ## Conversions tab Use this tab to see which campaigns, journeys, and in-apps lead users to a conversion, such as a purchase or a signup, and how much revenue they bring over the selected period. The tab counts the [conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/) you’ve chosen for the project. ### Set up conversion events Until you choose at least one event besides `PW_Conversion`, the tab shows the **See which campaigns lead to conversions** banner. Click **Set up events** and follow [Choose conversion events and track their revenue](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/#choose-conversion-events-and-track-their-revenue). To change the list later, click the **Conversion events** (gear) icon next to the filters. If the selected event has no conversions in the period, for example right after setup, the tab shows **Collecting data**. ### Filter conversions Use the filters above the tiles to choose what the tab counts: * **Event:** the conversion event to report on. * **Attribution:** the attribution window (1, 3, 7, or 30 days, 3 by default) and the contact type. Pushwoosh links each conversion to one campaign, the last one the user interacted with inside the window. The contact type sets what counts as an interaction: * **Click:** the last message the user opened or clicked, such as an opened push or Inbox message, a clicked email link, or a clicked button or submitted form in an in-app. * **Sent:** the last message sent to the user. In-apps get no conversions with this contact type, because they aren’t sent. * **Channel:** Push, Email, Inbox, In-app, SMS, or WhatsApp. SMS works only with **Sent**, and In-app only with **Click**. * **Campaign type:** one-time messages or journeys. [Instant in-apps](https://docs.pushwoosh.com/product/messaging-channels/in-apps/send-in-apps/send-instant-in-apps/) count as one-time messages, and in-app elements of a journey count toward the journey. * **Show bots:** count email opens and clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). Off by default. The switch isn’t saved, so it’s off again the next time you open the page. With the **Click** contact type, closing an in-app isn’t an interaction. If an in-app gets no clicks from any user, for example because it has no buttons, a view of it counts as the interaction. Rich media that opens from a push counts toward that push, not as an in-app. If users converted in the period but not after your messages, the tab shows **No conversions attributed to campaigns**. Click **Change attribution** to try a longer window. ### Conversion tiles The tiles show totals for the selected event, counting only conversions attributed to your campaigns: * **Conversions:** conversions attributed to your campaigns within the attribution window. * **Unique users:** users behind these conversions. Each user is counted once, however many times they converted. * **Revenue:** revenue from these conversions, with its share of the event’s total revenue. For an event that doesn’t track revenue yet, the tile offers **Set up revenue for this event**. * **Attribution share:** the share of all conversions of this event that are attributed to your campaigns. When the conversions carry more than one currency, a **Revenue** strip below the tiles replaces the **Revenue** tile. It lists the amount for each currency separately, with no exchange-rate calculation. Click a currency to show only campaigns with revenue in it. ![Conversions tab filters for event, attribution, channel, and campaign type above the Conversions, Unique users, and Attribution share tiles and a Revenue strip with USD and EUR amounts](https://docs.pushwoosh.com/overview-page-conversions-1.webp) ### Campaigns table The table lists every campaign with at least one attributed conversion, with its **Status**, **Sent**, **Open**, **Clicks**, **Conv.**, **Unique users**, and **Revenue** for the period. Each instant in-app is a row of its own. Clicks are available for email and in-apps only. For an in-app, **Open** counts how many times users saw it. **Clicks** counts button clicks and form submissions, and closing the in-app isn’t counted. **Sent** shows a dash because in-apps aren’t sent. ![Campaigns table on the Conversions tab listing journeys and one-time messages with status, sent, open, clicks, conversions, unique users, and revenue per currency](https://docs.pushwoosh.com/overview-page-conversions-2.webp) Click a journey row to open its breakdown by point. If the journey has several versions, pick one in **Journey versions**. In-app elements of the journey have the same **Open** and **Clicks** as instant in-apps. ![Journey breakdown drawer for Welcome series with Journey versions highlighted and a table of its push and email points](https://docs.pushwoosh.com/overview-page-conversions-3.webp) ## Export statistics Use **Export statistics** in the page header to download data as a CSV file. 1. Click **Export statistics**. 2. Set the export options in the **Export statistics from this project** modal: * **Period:** defaults to the range currently selected on the page. * **Message type:** Transactional and marketing, Only marketing, or Only transactional. * **Channel:** Push and/or Email. * **Source Type:** One-time messages and/or Customer journeys. * **Show bots:** turn on to include opens and clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners) in the email numbers of the file. Off by default. ![Export statistics from this project modal with Period, Message type, Channel, and Source Type fields](https://docs.pushwoosh.com/overview-page-export-2.webp) 3. Click **Export**. Get the CSV by email To get the same CSV by email on a daily, weekly, or monthly schedule, set up [auto-reports](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/auto-reports/). After you finish on Overview, open [User retention statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics/) for custom retention events, [Messaging statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/) for per-message detail, or [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) for custom charts. --- # Auto-reports **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/auto-reports/ Email the message statistics of Project overview as a CSV file on a daily, weekly, or monthly schedule to up to 25 recipients --- Use auto-reports to get the push and email statistics from the [Project overview](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) page by email on a schedule. Each report arrives as a CSV file, so you don’t have to export the statistics by hand. Choose how often the report is sent: every day, every week, or every month. Add up to 25 recipients, for example managers or an agency who don’t use the Control Panel. To download the same CSV once for any period, use [Export statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/#export-statistics) instead. ## Who can manage auto-reports Anyone who can see the project can see its auto-reports. To create, edit, pause, resume, delete, or send a report, a user needs permission to modify the project. Learn more about [user permissions](https://docs.pushwoosh.com/product/account-management-and-security/multi-login-accounts/). ## Where to find auto-reports You manage auto-reports in a drawer on the Project overview page. 1. Go to **Project overview**. 2. Click **Auto-reports** next to **Export statistics**. ![Auto-reports button highlighted in the Project overview header between Project settings and Export statistics](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-1.webp) To see when a report was last sent and when it goes out next, read **Last sent** and **Next sent** on its card in the drawer. **Last sent** appears after the first send, and **Next sent** only for an active report. Both times use the report’s timezone. ![Auto-report to CSV drawer with How to use and Create auto-report buttons and three report cards, one paused and two active](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-2.webp) ## Create an auto-report Set the schedule, the data, and the recipients of a new report in one form. A project can have up to 25 auto-reports. To create another one after that, delete a report you no longer need. 1. In the drawer, click **Create auto-report**. 2. In **Frequency**, choose **Daily**, **Weekly**, or **Monthly**. 3. For a weekly report, choose the **Day of the week**. For a monthly report, enter the **Day** of the month, from 1 to 31. 4. Set **Time to send**. 5. Choose the **Timezone**. It defaults to the timezone in your profile, or UTC if your profile has none. Check **Next report** under the timezone for the date and time of the next send. The report arrives within an hour after that time, usually in 5–10 minutes. ![Edit auto-report form with Weekly frequency, Monday, 09:00 AM, the Europe/Berlin timezone, and the Next report line](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-3.webp) 6. In **Message type**, choose **Transactional and marketing**, **Only transactional**, or **Only marketing**. 7. In **Channel**, select **Push**, **Email**, or both. 8. In **Source Type**, select **One-time messages**, **Customer journeys**, or both. ![Report data section with Message type Transactional and marketing, Push and Email selected, and One-time messages and Customer journeys selected](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-5.webp) 9. If the report includes **Email**, decide whether to count opens and clicks by [mail scanners](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#opens-and-clicks-from-mail-scanners). To count them, turn on **Show bots**. It’s off by default and changes only the email numbers. Pushwoosh saves this choice with the report. Every scheduled report uses it until you edit the report. 10. In **Report name**, keep the suggested name or type your own. Pushwoosh suggests a name from the frequency and channels, for example “Weekly stats push and email”. The name must be unique in the project. Names that differ only in letter case, such as “Weekly” and “weekly”, count as the same. If it’s taken, you see “An auto-report with this name already exists in the project”. Change the name and continue. 11. In **Recipients**, type an email address and press Enter. Repeat for each recipient. A report can have up to 25 recipients. Your own email is already added. You can add any email address. Recipients don’t need access to Pushwoosh. ![Delivery section with the report name Weekly stats push and email and two recipients, marketing@example.com and agency@example.com](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-6.webp) 12. Click **Create**. ## What period each report covers Each report contains the statistics for the last full period before the send day: * **Daily:** the previous day. * **Weekly:** the 7 days before the send day. A report sent on Monday covers the previous Monday through Sunday. * **Monthly:** the previous calendar month. The send day is taken in the report’s timezone, and the days of the period are counted in UTC. For example, a daily report sent on October 20 covers October 19 from 00:00 to 24:00 UTC. The schedule alone sets the period. Changing the date range on the **Project overview** page doesn’t affect the report. If you set a monthly report to a day the month doesn’t have, such as 31 in a 30-day month, it’s sent on the last day of that month. If nothing was sent in the period, the report still arrives. Its CSV file contains only the column headers. ## What the email contains The report arrives from Pushwoosh Team (). Its subject is the report name and the period, for example “Weekly stats push and email — Oct 13 – Oct 19, 2026”. The email contains: * **CSV attachment:** the file is attached if it’s up to 768 KB. A larger file is only available through **Download CSV**. * **Download CSV:** a link to the file. The link works for 7 days after the send. * **Open Project overview:** a link to the project in the Control Panel. * **Unsubscribe from this report:** a personal link for each recipient. It removes only that recipient from only this report. The CSV file has one row per message or journey message sent in the period, with these columns: * **Message details:** channel, type, title or subject, journey name, preset, status, and send time. * **Counts:** sent, delivered, opened, clicked, error, and unsubscribe. ## Manage auto-reports Open a card’s menu (**⋮**) to manage the report: * **Edit:** change any setting in the same form, then click **Save**. * **Pause:** stop scheduled sends. The report keeps its settings. * **Resume:** restart scheduled sends of a paused report. The next send is counted from the moment you resume, and sends missed during the pause aren’t made up. * **Send now:** build and email the report right away, without waiting for the schedule. * **Delete:** remove the report permanently after you confirm. To see a report’s schedule, channels, data filters, and recipients, click the arrow at the bottom right of its card. ![Expanded auto-report card showing schedule, channels, data filters, recipients, and the next send time](https://docs.pushwoosh.com/statistics-and-analytics-auto-reports-4.webp) ### Send a report now **Send now** builds and emails the report right away. It covers the same period a scheduled send would cover if it ran at that moment. For a daily report, that’s the previous day. * You can send each report manually once per hour. If you try again sooner, Pushwoosh tells you how many minutes to wait. * A manual send updates **Last sent**. The next scheduled send stays the same. * To send a report with no recipients, add a recipient first. **Send now** isn’t available without one. * To send a paused report, resume it first. Otherwise **Send now** shows the error “the report is paused, resume it to send” and doesn’t use up the hourly limit. ### Report statuses A report has one of these statuses: * **Active:** the report is sent on schedule. * **Paused:** scheduled sends are stopped until you resume the report. * **No recipients:** every recipient unsubscribed from the report, so nothing is sent. To restart it, click **Edit** in the card’s menu and add recipients. --- # Statistics FAQ **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/statistics-faq/ Find answers to common questions about Pushwoosh statistics, including data collection, reports, dashboards, and API access --- Below are the answers to commonly asked questions about Pushwoosh statistics. ## What statistics does Pushwoosh collect? Pushwoosh collects and stores statistics for all types of messages you send to your users through our platform. These statistics include detailed data on the performance of your campaigns, such as: * Detailed statistics for each element of customer journeys you create, as well as the overall journey statistics * Pushes sent, delivered, and opened for each segment, app, campaign, and the whole account * App installs, opens, and uninstalls for each app and platform * Devices registered and unregistered for each app and platform * Subscribers’ number dynamics for each app and platform * Send, delivery, open, and click-through rates for each email message * In-app impressions and interactions, as well as the number of times the in-app was skipped * Conversion rates and revenue generated by Events For more information about the data Pushwoosh collects, [read this post](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/what-data-does-pushwoosh-collect) ## What reports and dashboards does Pushwoosh provide? Pushwoosh provides comprehensive statistics on the performance of your applications and communications on various channels. You can access the following reports and dashboards: * [Customer Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics) * [Project overview](https://docs.pushwoosh.com/product/statistics-and-analytics/project-overview/) * [Dashboards](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/) (pre-built for Applications, Push Notifications, In-app Messaging, Emails, and Events + any Custom Dashboards you can build from metrics collected on the Pushwoosh platform) * [Message history](https://docs.pushwoosh.com/product/statistics-and-analytics/message-history) ## Can I monitor the performance of my campaigns in real time? Yes, [Pushwoosh Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) allows you to track the performance of your campaigns in real-time, directly on the journey canvas. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/) ## Is it possible to track the performance of each message in my customer journeys? Yes, you can directly access detailed statistics for each message within a campaign on the journey canvas, along with a clear overview of the overall performance of the entire campaign. Learn more about [push notifications](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics), [in-app](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/) and [email statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/). ## How will statistics change after I edit an active journey? When you edit and relaunch the journey, statistics keep being collected. Any new data is added to the data collected before the edits. To see how your journey performs before and after the edits, just switch between different time periods in the Customer Journey statistics panel. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/#select-a-time-period) Warning Remember, deleting a journey element means losing all its associated statistics permanently. If you need this data, save it before deleting. ## How can I track the overall performance of my application? You can easily do this with the pre-built **Application Dashboard**. To access the dashboard, navigate to **Statistics > Dashboards > Application**. The dashboard is designed to help you monitor your app’s performance and provides charts displaying key metrics. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#preconfigured-dashboards) ## How can I track my campaigns’ performance on specific channels (Push notification / Email / In-app campaigns) and by Events? You have pre-built dashboards for: * [Push notification campaigns](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/push-notification-statistics#understand-the-overall-performance-of-push-notifications) * [Email campaigns](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/#dashboards) * [In-app campaigns](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/in-app-statistics/#understand-overall-in-app-messaging-performance-trends) * [Events](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/#preconfigured-dashboards) ## Can I create custom dashboards that align with my business goals? With the Pushwoosh dashboard builder, you can create custom dashboards that include metrics relevant to your analytics goals. [Learn more](https://docs.pushwoosh.com/product/statistics-and-analytics/dashboards/managing-dashboards/) ## How often does Pushwoosh update statistics? Pushwoosh updates statistics in near real time, providing you with up-to-date analytics for your campaigns, including push notifications, emails, and other messaging channels shortly after interactions. ## Is there an API method for retrieving statistics for the messages I’ve sent? Pushwoosh offers several API methods for retrieving statistics for the messages you’ve sent. You can use Pushwoosh API to access various statistics related to your message campaigns, including: * [Customer Journey stats](https://docs.pushwoosh.com/developer/api-reference/customer-journey-api/statistics/) * [Push history stats](https://docs.pushwoosh.com/developer/api-reference/messages-api#getpushhistory) * [Pushes stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#messageslist) * [Email stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/message-statistics-api/#linksinteractions) * [Tags stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#gettagstats) * [Events stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/events-and-tags-statistics/#geteventstatistics) * [App stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/application-subscriber-statistics/) * [Campaign stats](https://docs.pushwoosh.com/developer/api-reference/statistics-api/campaign-statistics/) For more API methods for retrieving statistics, check [Pushwoosh API Reference](https://docs.pushwoosh.com/developer/api-reference/api-methods-reference-index). ## Can I export Pushwoosh statistics for further analysis or reporting? Yes, you have the option to download the data in CSV format. ## Can I get a daily email with statistics on my app? Absolutely! To receive daily performance emails, go to **Settings** > **Configure platforms** > **Edit project**, and enable **I want to receive Daily Application Performance Emails**. Click **Save project** to apply the changes. --- # Uplift report **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/ How Pushwoosh calculates a campaign or journey holdout uplift report, its treatment and control groups, and its update schedule --- The uplift report shows what a campaign or a journey achieved on its own. It compares the users who received it with the holdout, a group you set aside on purpose so they don’t get the message, and shows how much more often the first group reached the goal. A campaign is in the report only if you sent it with a holdout, a share of recipients you set aside. Your developers set it with the `holdout_percentage` parameter, from 1 to 20, in the [Messaging API](https://docs.pushwoosh.com/developer/api-reference/messaging-api-v2/notify/). A journey is in the report once for every [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) point it contains. ## How to get the report * **Journey:** open the Holdout Split point of the journey. It shows [Goal uplift vs holdout](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/#check-the-uplift-report) in the Control Panel. * **Campaign:** you can get the report only as a CSV file. The export runs through the API, so ask your developers to run [the export](https://docs.pushwoosh.com/developer/api-reference/statistics-api/export-uplift-report/). Give them the dates, up to 90 days, and, if you need only one, the campaign or journey. Without one, the file covers every campaign and journey of the application. [What’s in the file](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#whats-in-the-exported-file) Control groups have their own [Impact report](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/#view-the-impact-report) in **Settings**. It measures the application’s control groups, not a single campaign or journey. ## What counts as treatment and control The report compares two groups: **treatment**, the users who got the message, and **control**, the holdout, the users who didn’t. | | Treatment | Control | | ------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | **Campaign** | Every recipient who was sent the campaign. | Every recipient the holdout kept from getting it, as in the application’s [control groups](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/). | | **Journey** | Every user who went through the [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/#add-and-configure-holdout-split) point onto the **In journey** branch. | Every user routed to the **Holdout** branch of that point. | Users and recipients with no [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) are left out of both groups, because Pushwoosh can’t tell which group they belong to or match them to a conversion without it. ## Goal and attribution window **Campaign:** The goal and the attribution window (how long after a send a conversion still counts) come from the application’s Conversion Goals. When more than one is configured, the report uses the shortest attribution window among them, so a send is never credited with a distant, unrelated conversion. **Journey:** The goal is the journey’s own Goal points, and the window is the journey’s Conversion period setting (7 days if the journey doesn’t set one). The window starts when each user passes the Holdout Split point, because holdout users never get a send to start it from. Both exclude message-engagement events (opens, clicks, and similar) from the goal. The holdout group never receives a message to react to, so counting engagement would only show that a message went out, not what it achieved. [Control group analytics](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/#analyze-control-group-impact) excludes them for the same reason. ## Metrics in the report | Metric | What it means | | --------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Uplift %** | The percentage difference between the treatment and control conversion rates, relative to the control rate. Same formula as the Uplift of the application’s control groups. | | **Incremental events** | An estimate of how many more events the treatment group produced than it would have at the control group’s rate per user, counted for all treatment users. Same formula as [Conversions driven by messaging](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/#view-the-impact-report). | | **% of treatment** | Incremental events as a share of all events the treatment group generated. | | **Z-score, P-value, Confidence %** | **Confidence %** shows how sure the report is that the gap between the groups is not chance: the higher, the more you can trust the result. Z-score and P-value are the statistics behind it, from the same test the application’s control groups use. | | **Significance** | **Significant**, **Not significant**, or **Not enough data**. **Significant** means **Confidence %** is above 95%. It works both ways: a significant negative **Uplift %** means the message lowered the conversion rate. It reads as not enough data when either group has fewer than 10 conversions or fewer than 10 non-conversions. | | **Treatment revenue / Control revenue** | Money each group brought in on the report’s goal event, kept separately per currency. | | **Incremental revenue** | The money version of Incremental events: the difference in revenue per user between treatment and control, counted for all treatment users, per currency. | | **Revenue tracked** | Whether the revenue source for this event was actually configured, not guessed. See [Revenue in the report](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#revenue-in-the-report). | For example, a journey sends a welcome message to 90,000 users and holds back 10,000. In the **In journey** group, 4,500 users reach the goal (5%). In the **Holdout** group, 400 do (4%). The report shows: | Metric | Value | | ---------------- | -------------------------------------------------------------------------------- | | **Uplift %** | 25%, because 5% is a quarter more than 4% | | **Significance** | **Significant**, because with groups this large the gap is unlikely to be chance | Note **Uplift %** counts each user once, so one unusually active user can’t skew it. **Incremental events** counts every event, without capping per user, as the application’s control groups do, so a single very active treatment user can raise that number without moving **Uplift %**. When the two disagree, trust **Uplift %**. ### What’s in the exported file Each row of the file is one campaign, or one Holdout Split point of a journey, for one goal event. A row contains: * the campaign or journey and the goal event; * the number of users and conversions in each group; * the metrics from the table above; * the revenue for each currency. For each campaign, Holdout Split point, and goal event, the file holds the latest daily calculation within the dates you chose. See [How far back the report can look](https://docs.pushwoosh.com/product/statistics-and-analytics/uplift-report/#how-far-back-the-report-can-look). For the journey from the example above, the main columns look like this: | Column | Example | What it means | | ----------------------- | ------------------ | ------------------------------------------------------- | | `owner_type` | `TYPE_JOURNEY` | The row is about a journey, not a campaign. | | `event_name` | `purchase` | The goal event. | | `treatment_users` | 90000 | Users in the **In journey** group. | | `control_users` | 10000 | Users in the **Holdout** group. | | `treatment_conversions` | 4500 | Users in the **In journey** group who reached the goal. | | `control_conversions` | 400 | Users in the **Holdout** group who reached the goal. | | `uplift_pct` | 25.00 | **Uplift %**. | | `significance` | `TYPE_SIGNIFICANT` | **Significant**. | ## Revenue in the report Treatment, control, and incremental revenue are counted for the same goal event, the same users, and the same attribution window as the conversion numbers in the report. ### Revenue is kept per currency, never converted Revenue isn’t converted between currencies, because Pushwoosh keeps only today’s exchange rates, and converting an older window at today’s rate would be a guess. **Treatment revenue**, **Control revenue**, and **Incremental revenue** each hold one amount per currency instead of one combined total. Revenue from an event that doesn’t name a currency is listed under `unknown`. ### Revenue tracked tells you whether the source was configured **Revenue tracked** shows whether revenue was set up for the goal event, so an application that never set it up reads as unmeasured, not as zero revenue. Pushwoosh reads an event’s revenue from three places, in this order: 1. The built-in `PW_Conversion` event’s own `value` and `currency` fields. 2. An event revenue mapping you configured for that event. 3. The legacy `__amount` and `__currency` attributes on the event, if neither of the above applies. **Revenue tracked** is on only for the first two sources. With the third, the report still shows the amount but leaves the flag off, because nobody configured revenue for that event. ## When the report updates The report isn’t real time. It’s recalculated once a day, at 01:30 UTC, so a campaign sent or a journey point passed today shows up in the report tomorrow. Only running journeys with at least one Goal point are recalculated. When a journey stops, the Holdout Split point keeps showing its last numbers. ## How far back the report can look Each daily calculation counts campaign sends from about the last 45 days and Holdout Split passes from the last 180 days. Older sends and passes drop out of the next calculation, so a campaign is no longer recalculated about 45 days after it was sent. Pushwoosh keeps every daily calculation for 90 days. The Holdout Split point shows the latest one. An export shows the latest one within the dates you give, so an export for dates two months ago shows the numbers as they were then, for sends and passes before that. --- # User retention statistics **URL:** https://docs.pushwoosh.com/product/statistics-and-analytics/user-retention-statistics/ Learn how to analyze user retention by tracking user actions and events to measure engagement and optimize communication strategies --- The **Retention** tab in Pushwoosh helps you understand how users interact with your app over time. By tracking retention to specific actions or events relevant to your business, you can gauge the effectiveness of your communication strategies. To analyze user retention data, navigate to **Statistics > Retention** in your Pushwoosh account. ![Retention tab in Statistics section showing user retention analysis interface with event selection and date range options](https://docs.pushwoosh.com/statistics-and-analytics-user-retention-statistics-1.webp) ## How to analyze retention data ### Selecting events The first step involves defining user actions or events that signify activity and engagement. They will be used to build the retention graph. * Choose the event that marks the starting point for tracking retention. This could be an event like *ProductAdd* (adding a product to a cart) or *Account Registered*. * Select the returning or subsequent action that indicates a user’s return and continued engagement. For example, it can be *CheckoutSuccess* (completing a purchase) or *App Open* (opening the app). **Example:** To understand how effectively your app converts users who add products to their cart into loyal customers, set *ProductAdd* as the initial event and *CheckoutSuccess* as the returning event. This will help you identify drop-off points and optimize the user journey to maximize retention. ### Setting the date range Specify the time frame for which you want to analyze user retention data. By default, Pushwoosh stores event data for one year, however, if you need longer storage, [contact Pushwoosh sales.](https://www.pushwoosh.com/demo/) ![Date range selector for retention analysis with calendar picker showing default one-year data storage period](https://docs.pushwoosh.com/statistics-and-analytics-user-retention-statistics-2.webp) ### Understanding the User Retention Graph **The Retention Graph** (also known as User Retention Curve) is a visual representation of user retention over time. It displays the average retention rate for the chosen date range. The graph shows how many days, on average, users wait before performing the returning event after the initial event. [Learn more about the User Retention Curve and how to flatten it](https://blog.pushwoosh.com/blog/flatten-user-retention-curve/) ![User Retention Graph displaying retention curve showing average retention rate over time with days and percentage axes](https://docs.pushwoosh.com/statistics-and-analytics-user-retention-statistics-3.webp) ### Detailed Retention Data table Below the graph, a detailed table breaks down user retention rates for each day within the date range. Columns display the total number of users within a cohort and the retention rate for each day following the initial event (e.g., Day 1, Day 2, etc.). ![Detailed Retention Data table showing breakdown of user retention rates by day with cohort size and daily percentages](https://docs.pushwoosh.com/statistics-and-analytics-user-retention-statistics-4.webp) ### Refreshing data Click **Refresh Data** to ensure you’re viewing the most up-to-date information. ![Refresh Data button in retention statistics interface for updating retention analysis with latest event data](https://docs.pushwoosh.com/statistics-and-analytics-user-retention-statistics-5.webp) ## Use cases Here are some common use cases for analyzing user retention in Pushwoosh: ### Basic app retention **Goal:** Measure how long your audience stays with you. **Initial Event:** PW_DeviceRegistered **Returning Event:** PW_ApplicationOpen **Analysis:** This combination shows how many users actually return and use the app after their device first gets a push token. In iOS and Android apps, [PW_DeviceRegistered](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered) fires even for users who don’t allow notifications. A declining retention rate after the first visit might indicate issues with onboarding or a lack of value proposition. ### App value retention (e.g., Fitness apps) **Goal:** Evaluate if users complete a valuable action in your app—in our example, exercise regularly. **Initial Event:** Workout Complete **Returning Event:** Workout Complete **Analysis:** To track how consistently users complete workouts over time, you can mark *Workout Complete* as both the initial and returning event. If the retention rate is low, it may indicate a need to revamp the app’s engagement strategies. This can be achieved by optimizing motivational notifications to boost user activity. ### Content engagement **Goal:** Evaluate the duration for which the new audience remains engaged with your content. **Initial Event:** Notification Open (when a user opens a push notification or email) **Returning Event:** Notification Open (when a user opens another push notification or email) **Analysis:** The retention graph will show the percentage of users who open subsequent push notifications or emails after opening an initial one. This helps you determine if your content is interesting enough to keep users engaged over time. To learn how to set up a retention campaign in Pushwoosh, [read this guide](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh) --- # Troubleshooting overview **URL:** https://docs.pushwoosh.com/product/troubleshooting/ Find out why a message didn't reach some recipients or why users dropped off a Customer Journey, and how to fix each cause --- This section helps you find out why a message didn’t reach some recipients or why users dropped off a Customer Journey, and what to do about it. Pick the page that matches what you see. [How to read the Errors tab ](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) [Start here if a message has failed sends. Learn what the Errors tab of Message stats shows and how to look up an error.](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/) [Sending errors ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) [What each error means and how to fix it, by platform, from iOS and Android to email, SMS, and messengers.](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/) [Drop-off and skip reasons in Customer Journey ](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/) [What each drop-off and skip reason in a Customer Journey means and how to fix it.](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/) --- # Drop-off and skip reasons in Customer Journey **URL:** https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/ Solve common drop-off issues in Customer Journey by identifying and resolving account, device, user, time delay, and configuration problems --- By double-clicking on the point of a launched Journey, you can see the reasons for drop-offs and the number of drop-offs for each cause. A couple of events don’t show up in that panel at all: they’re recorded as **Skipped** in the point’s statistics instead. See [Skip reasons](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#skip-reasons). ## Drop-off reasons These errors remove the traveler from the Journey. ### Account suspended **What it means:** The account is disabled, so the Pushwoosh API is not available. Most often this pauses the whole Journey before it reaches the next traveler. If the account is suspended after a step has already started sending, that specific step can also drop the affected traveler with this same reason. **What to do:** Contact the [Customer Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/?utm_content=docs&utm_term=post&utm_campaign=troubleshooting) or your dedicated Customer Success Manager to restore access. If the Journey was paused, resume it once access is restored. ### Emails reached limits **What it means:** Your account’s email sending limit has been exceeded, so this email couldn’t be sent. **What to do:** Contact the [Customer Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/?utm_content=docs&utm_term=post&utm_campaign=troubleshooting) or your Customer Success Manager to raise the limit. ### Internal error **What it means:** A general error indicator meaning the Journey could not proceed for this traveler at this step. Common causes: * The Event attribute referenced by a [**Time delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) element is missing, or its value isn’t in the expected date format. A missing Tag in the same element results in [Tag not found](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#tag-not-found) instead, but a Tag that exists with a value in the wrong format falls here too. * The traveler’s UserID changed while the Journey was in progress, so the system can no longer find the profile to update Tags. * An issue with the [push preset](https://docs.pushwoosh.com/product/content/push-presets/) or template used in a [**Send Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element. **What to do:** For the Time delay cause, make sure the Tag is of the[ Date type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#types-of-tags) with values in 2006-01-02T15:04:05 format (in UTC), or that you[ created the Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#1-create-events-in-pushwoosh-control-panel) with an [attribute of the Date type](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#2-set-attributes) and values in 2006-01-02 15:04:05 format when sent via [/postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api#postevent). For the other causes, review the configuration of the affected element and the data of the travelers entering that step, or contact the [Customer Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/?utm_content=docs&utm_term=post&utm_campaign=troubleshooting) with the Journey and element details. ### User mismatch **What it means:** The device’s current UserID doesn’t match the UserID recorded for it in the Journey. Note For example, the client’s email is recorded in the UserID parameter and then mistakenly assigned to another user. If you try to set a user-specific Tag in the [Update user profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) element, the error will occur, as the Tag is considered to have already been assigned to another user. **What to do:** Check the device registration process and make sure a UserID isn’t transferred to another user while the device is still active in a Journey. ### Device not found **What it means:** Pushwoosh has no device record for the [HWID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/device-identifiers/#hwid) that entered the Journey. This can happen if: * The HWID is only known to a system outside Pushwoosh and was never passed to Pushwoosh, yet an Event somehow still triggered the Journey. * The device was registered before, but its HWID was later erased from the database. **What to do:** Check the device registration process and make sure devices register correctly before they trigger the Event that enters the Journey. ### User not found **What it means:** Pushwoosh has no user record for the [UserID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/) that entered the Journey. This can happen if: * The UserID is only known to a system outside Pushwoosh and was never passed to Pushwoosh, yet an event somehow still triggered the Journey. * The user was registered before, but their UserID was later erased from the database. * The UserID is an email address that was never [associated with the device](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/), so Pushwoosh doesn’t recognize the user and the email recipient as the same person. **What to do:** Check your user registration process and make sure UserID values aren’t mistakenly erased from the database. If you use email addresses as UserIDs, make sure your development team associates them with the device. ### Filter not found **What it means:** The [Segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) used by the Journey existed when it was created, but was deleted after the Journey was activated. Unlike the other errors on this page, this stops the entire Journey, not just the affected traveler. **What to do:** A finished Journey can’t be restarted: clone the Journey, point the clone to another Segment (or recreate the deleted one), and launch the clone. ### Rich media not found **What it means:** The rich media used in this element existed when the Journey was created but was deleted after the Journey activation. **What to do:** Select another rich media (or recreate the deleted one) in the element. ### Tag not found **What it means:** A Tag used in a [**Time delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) element isn’t assigned to some travelers, so they drop off at that step. **What to do:** Verify the Tag is assigned to travelers correctly and hasn’t been deleted, or set up **Time delay** differently so fewer travelers drop off. Note This doesn’t apply to the [**Update user profile**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) element: if the Tag it sets was deleted after the Journey was activated, that Tag update is silently skipped, and the traveler is not dropped. ### Invalid push preset **What it means:** The push preset used in a message element existed when the Journey was created but was deleted after the Journey activation. This pauses the whole Journey, not just the affected traveler. **What to do:** Select another preset (or recreate the deleted one) in the element, then resume the paused Journey. ### Content not approved **What it means:** The push preset or email template used in a [**Send Push**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) or [**Send Email**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) element was **Approved** when the Journey last launched or resumed, but stopped being **Approved** while the Journey was already running, for example after an edit created a new **Draft** version. Content that’s already unapproved before launch or resume is caught earlier by the [pre-launch check](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#pre-launch-check), which blocks the Journey from launching or resuming until you fix it. A **Send Push** element also pauses the whole Journey, not just the affected traveler. A **Send Email** element only drops the affected traveler and leaves the Journey running. **What to do:** Open the preset or template, submit it for approval, and wait until its status is **Approved**. If a **Send Push** element paused the Journey, resume it once the content is approved. ### Time has already passed **What it means:** The time calculated for a [**Time delay**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) element, based on the Event attribute or the Tag specified, was already in the past. **What to do:** Set up **Time delay** differently, or check the date value in the Event attribute or Tag used. ### No vouchers left in the pool **What it means:** There are no unused vouchers left in the selected pool. This includes vouchers already sent, as well as any [deleted from the pool](https://docs.pushwoosh.com/product/content/vouchers#delete-vouchers). **What to do:** [Add more vouchers](https://docs.pushwoosh.com/product/content/vouchers#add-vouchers-to-an-existing-pool) to the pool. ### Voucher pool has been deleted **What it means:** The voucher pool used in the element existed when the Journey was created but was deleted after the Journey activation. **What to do:** Connect a different voucher pool in the element. A deleted pool can’t be restored. ### Voucher pool has expired **What it means:** The pool’s expiration date has passed, so vouchers from it can no longer be sent. **What to do:** [Create a new voucher pool](https://docs.pushwoosh.com/product/content/vouchers#create-a-voucher-pool) with a later (or no) expiration date and connect it in the element, or connect a different pool that hasn’t expired. ### Request error **What it means:** The [**Webhook**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/) element’s request failed to complete: a network error, a timeout, an invalid or blocked destination URL (private, internal, loopback, or link-local, including cloud metadata), the account’s webhook rate limit being exceeded, the destination cooling down after repeated failures, or a non-2xx response from the destination (after Pushwoosh’s own retry, on `500`, `502`, `503`, or `504`). If the URL or request body template fails to render before the request is sent, that’s classified as [Internal error](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#internal-error) instead; invalid headers in the element are silently dropped from the request rather than causing either error. **What to do:** Check that the destination URL is correct, public, and reachable, and that it returns a successful (2xx) response. See [Timeout, retries, and failed requests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/webhook/#timeout-retries-and-failed-requests) for the exact conditions. ### No User ID **What it means:** The traveler reached an [**Issue wallet pass**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element with no [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). A wallet pass is issued to a user, not an anonymous device, so there’s nobody to issue it to. This isn’t retried. **What to do:** Check your app’s user registration flow and make sure it sets a User ID before travelers reach this element. ### Wallet pass wasn’t issued **What it means:** An [**Issue wallet pass**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element couldn’t issue a pass from one of its configured campaigns, for example because the campaign was deleted or archived. This isn’t retried. **What to do:** Open the element and check that its configured campaigns still exist and aren’t archived. A Draft campaign issues passes fine, it doesn’t need to be Active. Select a different campaign if needed. A deleted campaign can’t be restored. ## Skip reasons These events don’t appear in the Drop-offs panel: the traveler continues to the next step as usual, and the point’s statistics record them as **Skipped** instead. ### Frequency capping reached **What it means:** The traveler already reached the [frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) limit at this step. This is expected behavior: the message is skipped, but the traveler continues in the Journey. **What to do:** No action is needed. To change how often this happens, adjust your frequency capping settings. ### No match identifiers **What it means:** In an [**Audience sync**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/audience-sync/) element, the traveler has none of the identifiers required to match the selected audience. The traveler is skipped for this step and continues in the Journey. **What to do:** No action is needed for that traveler. To reduce how often this happens, configure user data mapping (**Map user data**) in your Meta Ads integration settings for better identifier match quality. --- # About sending errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/ Look up a notification sending error by platform, see where the errors of a message appear, and learn how to fix each error --- A sending error means a message wasn’t sent to a recipient. The pages in this section list the errors by platform and explain what each one means and how to fix it. ## Where to see sending errors Open the Message stats page of the message and click **Errors**. The tab is titled **Delivery errors by platform**, and each delivery error on it is a sending error. Failed sends are grouped by platform. Each row shows the error name and code, for example **Invalid To Phone Number (1001)**, and how many sends failed with it. Some errors can appear on any platform, for example **Push Token Not Found** or **Skip Push**. They are listed in [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform), together with the error categories and how to export failed sends. ## Find your error Look up the error on the page for the platform its row is grouped under. ### Push notifications [iOS, Mac OS X, Safari ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/ios-os-x-safari-errors/) [Android, Chrome, Firefox ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/android-chrome-firefox-errors/) [Huawei ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/huawei-errors/) [Windows ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/windows-errors/) [Amazon ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/amazon-errors/) ### Email [Email ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/email-errors/) ### SMS and messengers [SMS and messengers ](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/) If the error isn’t on the platform page or in [Errors on every platform](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform), contact Pushwoosh support and include the error name and code. If users dropped off or were skipped at a point of a Customer Journey, see [Drop-off and skip reasons in Customer Journey](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/) instead. --- # Amazon errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/amazon-errors/ Identify and fix common Amazon push errors to ensure your notifications reach users and keep your campaigns performing well. --- These errors come from Amazon Device Messaging (ADM) when Pushwoosh sends push notifications to Amazon devices. | Error | Description | What to do | | ------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | | **ADMFailed** | An internal ADM failure caused notification delivery to fail. | Try sending the message again later. | | **ADMUnavailable** | The ADM server is currently unavailable. | Try sending the message again later. | | **BadRequest** | Failed to complete the request to Amazon. | Try sending the message again later. | | **FailedToGetADMToken** | ADM authentication failed. | Try sending the message again later. | | **FrequencyCapping** | The device was filtered by [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). | If such behavior was not intended, check the [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) settings. | | **InvalidRegistrationId** | The push token is invalid; the device will be unsubscribed from notifications. | Use a valid token. | | **Unregistered** | The push token is invalid; the device will be unsubscribed from notifications. | Try using a new token. | --- # Android, Chrome, Firefox errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/android-chrome-firefox-errors/ Common error messages and solutions for push notifications on Android, Chrome, and Firefox platforms to resolve delivery issues. --- These errors come from Firebase Cloud Messaging (FCM) when Pushwoosh sends push notifications to Android, Chrome, or Firefox devices. | Error | Description | What to do | | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **AuthenticationError** | FCM authentication failed due to invalid credentials. | Check that the credentials, ServerKey, and SenderID specified in the Pushwoosh Control Panel are correct. | | **DisabledFirebaseAPI** | Sending pushes via the new HTTP v1 protocol is disabled in the FCM console. | Enable sending push notifications in the FCM Console using the new HTTP v1 protocol. | | **EmptyCredentials** | Failed to send a push notification using any of the specified credentials. The credentials specified in the Pushwoosh Control Panel are invalid. | Reconfigure the platform (*Settings* → *Configure platform*). | | **FCMInternalServerError** | FCM failed to deliver the message to the device. This usually means that the FCM service is temporarily unavailable. | Try again after a while. | | **FrequencyCapping** | The device was filtered by [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). | If such behavior was not intended, check the [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) settings. | | **InternalServerError** | FCM failed to deliver the message to the device. This usually means that the FCM service is temporarily unavailable. | Try again after a while. | | **InvalidCredentials** | When configuring the platform, you added incorrect JSON with FCM credentials or specified incorrect SenderID or ServerKey. | Reconfigure the platform using the correct credentials, SenderID, and ServerKey from the FCM Console. | | **InvalidEndpoint** | This is an internal FCM error that means the push is sent to an invalid address. | Try again after a while. | | **InvalidPrivateKey** | The private key specified in the *private_key* field in the FCM credentials has an invalid structure. | Reconfigure the platform in the Pushoowsh Control Panel and re-upload JSON with credentials. | | **InvalidRegistration** | The push token is invalid; the device will be unsubscribed from notifications. | Use the valid token and check whether you have enabled notifications from your app on the device. | | **MessageTooBig** | Message size exceeds the 4096 bytes FCM limit. | Reduce the size of the message you send. | | **MistmatchSenderId** | The error occurs for one of the following reasons:- The sender ID specified in your app does not match the sender ID specified in the Pushwoosh Control Panel when configuring the platform. - The sender ID in your app does not match the sender ID associated with the FCM credentials you used. | Ensure you are using a valid sender ID from Firebase Console in your application and Pushwoosh platform configuration. | | **NotFound** | This is an internal FCM error that means the FCM service is currently unavailable. | Try again after a while. | | **NotRegistered** | Device is not registered. This status means one of the following:- The device was unsubscribed from notifications; - The application was deleted from the device; - The push token of the device has expired. | Try using a new token. | | **PermissionDenied** | Credentials specified in the Pushwoosh Control Panel do not grant permission to send push notifications. This means that push notifications cannot be sent from this FCM account. | Get the credentials with the correct permissions according to the [Firebase documentation](https://firebase.google.com/docs/projects/iam/permissions). When generating credentials to send messages, you must have the Firebase Admin role or a role with the *‘cloudmessaging.messages.create’* permission. | | **SenderIDMistmatch** | The error occurs for one of the following reasons:- The sender ID specified in your app does not match the sender ID specified in the Pushwoosh Control Panel when configuring the platform. - The sender ID in your app does not match the sender ID associated with the FCM credentials you used. | Ensure you are using a valid sender ID from Firebase Console in your application and Pushwoosh platform configuration. | | **TokenIsEmpty** | The following reasons can cause the error:- The device does not have a token. - The token was not transmitted when registering the device. - When the request was sent, the device did not have a token, but for some reason, it was transmitted to our device database. | If you see this error frequently, please contact our [Customer Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/?utm_content=docs&utm_term=post&utm_campaign=android-chrome-firefox-errors). | | **Unavailable** | Failed to send the message to FCM after several attempts. This usually means that the FCM service is temporarily unavailable. | Try again after a while.If you see this error frequently, please contact our [Customer Support Team](https://help.pushwoosh.com/hc/en-us/requests/new/?utm_content=docs&utm_term=post&utm_campaign=android-chrome-firefox-errors). | | **Unregistered** | Device is not registered. This status means one of the following:- The device was unsubscribed from notifications; - The application was deleted from the device; - The push token of the device has expired. | Try using a new token. | | **UnsupportedTokenType** | The token is invalid; the device will be unsubscribed from notifications. | Use the valid token. | --- # Email errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/email-errors/ Discover common Pushwoosh email errors — from template and content issues to account, delivery, and SMTP errors — and learn how to fix them. --- This page lists the error codes Pushwoosh can return when sending an email, grouped by what caused them. Each entry explains what the error means and how to resolve it. ## Account and sending limits These errors mean your account or configuration set hit a sending cap, pause, or suspension — nothing is wrong with the message itself. | Error | Description | What to do | | --------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **AccountDailyQuotaExceeded** | Your account has reached its daily email sending quota. | Wait for the quota to reset, or contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to request a higher quota. | | **AccountSendingPaused** / **AccountSendingPausedException** / **ConfigurationSetSendingPaused** / **ConfigurationSetSendingPausedException** | Email sending has been paused for your account or configuration set, usually due to a high bounce or complaint rate. | Review your [bounce and complaint rates](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#bounce-management) and contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to resume sending. To catch rising bounces before sending is paused, watch for a [bounce spike warning](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#bounce-spike-warning-before-email-sending-is-paused) in the Notification center. | | **AccountSuspended** | Your account’s ability to send emails has been suspended. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to find out why and how to restore sending. | | **AccountThrottled** | Too many emails were sent in a short period, and the sending rate limit was exceeded. | Reduce your sending rate, or contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to request a higher rate limit. | ## Sender and domain configuration These errors point to a problem with how your sending domain or “From” address is set up and verified. | Error | Description | What to do | | ---------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CannotGetCredentials** | Pushwoosh couldn’t get the credentials needed to send the email. | Check that your [sender domain](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) is verified and active. | | **FromEmailAddressNotVerifiedException** | The sender address isn’t verified and can’t be used to send this email. | [Verify the sender’s domain and email address](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) and try again. | | **From email domain is not verified** (1296) | The “From” domain hasn’t completed DNS verification, so the message (including test emails) can’t be created. | Check the domain’s status in [Your sender domains](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#your-sender-domains) and add any records still marked **Pending**. | | **Reply email domain is not verified** (1297) | The reply-to domain hasn’t completed DNS verification, so the message (including test emails) can’t be created. | Check the domain’s status in [Your sender domains](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#your-sender-domains) and add any records still marked **Pending**. | | **FromEmailMissing** | The “From” email address is missing or invalid. | Set a valid, verified “From” email address in the [email platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) or in the message settings. | | **FromNameMissing** | The “From Name” for the email is missing or invalid. | Set a valid “From Name” in the [email platform configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/) or in the message settings. | | **MailFromDomainNotVerified** / **MailFromDomainNotVerifiedException** | The message couldn’t be sent because Pushwoosh couldn’t find the required MX record for the sending domain. | Check the [DNS/DKIM records](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required) for your sending domain and make sure they’re published correctly. | | **InvalidTrackingOptionsException** | The custom tracking domain configured for open/click tracking is invalid. | Check your [custom tracking domain settings](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#configure-link-tracking). | ## Recipient address issues These errors are caused by the recipient’s email address itself — its format, or its bounce and complaint history — not by your account or template. | Error | Description | What to do | | -------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | | **AddressContainsControlOrWhitespace** | The email address contains control characters or extra whitespace. | Remove any hidden characters or whitespace from the email address and try again. | | **EmailComplaint** | The recipient previously marked a message from you as spam. | Remove this recipient from future sends to protect your sender reputation. | | **EmailIsBounced** | The message wasn’t sent because this email address has already bounced. | Use a valid, active email address, or remove this recipient from your mailing list. | | **IllegalEmailAddress** | The recipient’s email address has an invalid format. | Check the recipient’s email address and correct it. | | **UnableToParseEmailBCC** | The BCC field contains an invalid value. | Check the format of the addresses in the BCC field. | ## SMTP delivery errors These errors happen while Pushwoosh delivers the message over SMTP, from a missing configuration to a failed connection. | Error | Description | What to do | | -------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **SMTPCannotProcessData** | Pushwoosh couldn’t build the email for SMTP delivery. | Check the email template settings. | | **SMTPCannotProcessFrom** | The SMTP “FROM” field is empty. | Set a valid “From” email address for the message. | | **SMTPCannotProcessRcpt** | The SMTP “RCPT” (recipient) field is empty. | Check that the recipient’s email address is set correctly. | | **SMTPCannotProcessSending** | An internal error occurred while sending the message via SMTP. | Try sending the message again later. If the error persists, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **SMTPIsNotConfigured** | SMTP hasn’t been set up for this account. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to configure SMTP sending. | | **SMTPProviderBlocked** (1138) | Pushwoosh paused sending to this recipient’s email provider from your account after too many bounces and errors in a short period. | Wait for sending to resume, or contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if this persists. | | **SMTPUnableToCreateConnection** | Pushwoosh couldn’t establish an SMTP connection. | Check your SMTP server settings and credentials, then try again. | | **SMTPUnableToSignDKIM** | Pushwoosh couldn’t sign the message with DKIM. | Check the [DKIM configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#dkim-records-required) for your sending domain. | | **SMTPUnknownError** | An unspecified SMTP error occurred. | Try sending the message again. If the error persists, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **SMTPUnknownProvider** (1139) | Pushwoosh couldn’t determine the email provider for the recipient’s address. | Check that the recipient’s email address is correctly formatted, or contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if the address looks correct. | ## Template and content errors These errors occur while Pushwoosh builds or renders the email — a missing template, invalid placeholder data, or malformed HTML. | Error | Description | What to do | | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CannotGetEmailPlaceholders** | Pushwoosh couldn’t fetch the placeholders used in the email template. | Check the [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content) placeholders in the email template. | | **CannotGetTemplateBindings** | Pushwoosh couldn’t fetch the data bindings required to render the template. | Check the [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content) and [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) settings used in the template. | | **CannotNormalizeHTML** | The email content couldn’t be prepared for sending. | Check the email template’s HTML content for errors. | | **CannotProcessEmailTemplateFromRepo** | The email template couldn’t be loaded and processed. | Check the email template settings and try again. | | **EmailCannotGetEmailTemplateCode** | Failed to get `email_template_code` or it is empty. | Check that the platform is configured correctly and try again. | | **EmailCannotGetEmailTemplateFromRepo** | Failed to get the email template by `email_template_code`. | Check that the platform is configured correctly and try again. | | **InvalidRenderingParameterException** | One of the values used to fill the template is invalid, often due to malformed JSON. | Check the [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content) data passed to the template. | | **InvalidTemplateException** | The specified email template couldn’t be rendered. | Check the email template for errors and try again. | | **MissingRenderingAttributeException** | A value referenced by a placeholder in the template wasn’t provided. | Make sure all placeholders used in the template have matching values. | | **TemplateDoesNotExistException** | The specified email template doesn’t exist. | Check that the email template is correctly configured and hasn’t been deleted. | | **TooDeepTemplateInsertion** | Templates are nested too deeply inside one another. | Reduce the number of nested template insertions in the email content. | | **UnableToBuildEmailAttachments** | One or more attachments couldn’t be built. | Check the attachments added to the email and try again. | | **UnableToBuildHTML** | Failed to compile the email template. Most likely, the template contains an error related to [Dynamic Content placeholders](https://docs.pushwoosh.com/product/personalization/dynamic-content) or [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/). | Check the email template settings. | | **UnableToCreateHTML** | Pushwoosh couldn’t generate the final HTML for the email. | Check the email template settings. | | **UnableToInsertTemplate** | Pushwoosh couldn’t insert a referenced template into the email. | Check the inserted/nested template reference in the email content. | | **UnableToMinifyHTML** | Pushwoosh couldn’t minify the email’s HTML content. | Check the email template’s HTML for malformed markup. | ## Liquid templates and Connected Content These errors are specific to Liquid syntax and to Connected Content requests used to pull in personalized data. | Error | Description | What to do | | ---------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------- | | **ConnectedContentInvalidJSON** | The [Connected Content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) endpoint returned invalid JSON. | Check that your backend returns valid JSON for the Connected Content request. | | **ConnectedContentInvalidParameters** | The number of parameters passed to [Connected Content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) doesn’t match the template. | Check the parameters passed in the Connected Content tag against your template. | | **ConnectedContentInvalidTemplate** | The [Connected Content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) tag contains an invalid Liquid template. | Check the Liquid syntax used inside the Connected Content tag. | | **ConnectedContentInvalidURL** | Pushwoosh couldn’t fetch the URL specified in [Connected Content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/). | Check that the Connected Content URL is correct and reachable. | | **ConnectedContentInvalidVarExpression** | The `${}` expression used in [Connected Content](https://docs.pushwoosh.com/product/personalization/liquid-templates/connected-content/) is invalid. | Check the `${}` syntax used in the Connected Content tag. | | **InvalidLiquidTemplate** | The email contains an invalid Liquid template. | Check the [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) syntax used in the email. | | **LiquidTemplateInvalidParsing** | The Liquid template couldn’t be parsed due to a syntax error. | Check the [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) syntax used in the email. | | **LiquidTemplateInvalidRendering** | The Liquid template couldn’t be rendered with the available data. | Check that the variables used in the [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/) match the data available for the recipient. | | **LiquidTemplateInvalidTimezone** | An invalid timezone was used with a date filter in the Liquid template. | Check the timezone value used with the date filter in your [Liquid Templates](https://docs.pushwoosh.com/product/personalization/liquid-templates/). | ## Delivery and infrastructure errors These errors come from the email sending infrastructure itself. Most are transient or internal, and several require Pushwoosh Support to investigate. | Error | Description | What to do | | --------------------------------- | ------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **CannotConnect** | Pushwoosh couldn’t connect to the email sending gateway. | Try sending the message again later. If the error persists, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **CannotGetDeviceAPIURL** | Pushwoosh couldn’t determine the API URL required to send the email. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **CannotSend** | The email couldn’t be sent. | Try sending the message again later. If the error persists, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **EmailSESInvalidParameterValue** | One of the parameters passed to Amazon is incorrect. | Check the passed parameters and try again. | | **Failed** | The message couldn’t be sent for an internal reason. | Try sending the message again. If the error persists, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **MessageRejected** | Amazon rejected the message, and it wasn’t sent. | Check the message content and recipient address, then try again. | | **RequestTimeout** | The request to the email sending service timed out. | Try sending the message again later. | | **SpamDetected** | The message was flagged as spam and wasn’t sent. | Review your content for spam-like characteristics and contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) if you believe this is a mistake. | | **TransientFailure** | A temporary failure occurred on Amazon’s side while sending the message. | Try sending the message again later. | | **UnableToUnmarshalMetaData** | Pushwoosh couldn’t process the metadata attached to the email. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message ID. | | **UnknownError** | An unspecified sending error occurred. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message ID for investigation. | ## Amazon SES configuration errors These errors come from Pushwoosh’s internal Amazon SES sending infrastructure, not from your message content or account settings. | Error | Description | What to do | | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **Amazon SES configuration errors**- `ConfigurationSetAlreadyExistsException` - `ConfigurationSetDoesNotExistException` - `EventDestinationAlreadyExistsException` - `EventDestinationDoesNotExistException` - `InvalidCloudWatchDestinationException` - `InvalidConfigurationSetException` - `InvalidDeliveryOptionsException` - `InvalidFirehoseDestinationException` - `InvalidLambdaFunctionException` - `InvalidPolicyException` - `InvalidS3ConfigurationException` - `InvalidSNSDestinationException` - `InvalidSnsTopicException` - `RuleDoesNotExistException` - `RuleSetDoesNotExistException` - `TrackingOptionsAlreadyExistsException` - `TrackingOptionsDoesNotExistException` - `CustomVerificationEmailInvalidContentException` - `CustomVerificationEmailTemplateAlreadyExistsException` - `CustomVerificationEmailTemplateDoesNotExistException` - `LimitExceededException` - `ProductionAccessNotGrantedException` - `AlreadyExistsException` - `CannotDeleteException` - `InvalidSendingPoolName` | An internal error in Pushwoosh’s Amazon SES sending infrastructure. This isn’t caused by your message content or account settings. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the exact error name and the time the message was sent. | --- # Huawei errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/huawei-errors/ Learn how to troubleshoot Huawei push notification errors with detailed explanations and recommended fixes for common issues. --- These errors come from Huawei Push Kit when Pushwoosh sends push notifications to Huawei devices. | Error | Description | What to do | | --------------------------------------- | ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **AllTokensAreInvalid** | The push token is invalid; the device will be unsubscribed from notifications. | Use the valid token. | | **BodyLargerThanDefaultValue** | The message size exceeds 4096 bytes. | Reduce the message body size. | | **ErrorWhileSendingToHuawei** | Failed to complete the request to Huawei. | Try sending a push later. | | **MessageContainsSensitiveInformation** | The message body contains sensitive information. | Check your message’s content to ensure that the information you share does not violate Huawei policies. | | **NoPermissionToSend** | You cannot send push notifications to the Huawei platform with the specified credentials. | Reconfigure the Huawei platform using the new credentials. | | **UnableToGetAuthToken** | Failed to get the Huawei authorization token. | Check the platform configuration in the Pushwoosh Control Panel. | | **UnableToMarshalResponse** | Incorrect response body from Huawei. | If you cannot check if a push was sent in this case, it’s best not to send the same message again. This error is rare, so it is unlikely that it will happen again in the future. | --- # iOS, Mac OS X, Safari errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/ios-os-x-safari-errors/ Troubleshoot iOS, Safari, and macOS push issues to keep your campaigns running smoothly and ensure notifications reach users. --- These errors come from Apple’s APNS service when Pushwoosh sends push notifications to iOS, macOS, or Safari devices. | Error | Description | What to do | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **BadCertificate** | The APNS certificate for this application is invalid. | Create a valid certificate using [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration/#generating-a-certificate-request-file) and check that the platform is configured correctly. | | **BadDeviceToken** | The specified device token is invalid. Therefore, the device will be unsubscribed from notifications. | Verify that the request contains a valid token and that the token matches the environment. | | **DeviceTokenNotForTopic** | The specified token is unsuitable for sending notifications to the specified topic (package name specified when creating the iOS app). The device will be unsubscribed from notifications. | Use the token that is suitable for sending notifications to this topic. | | **ExpiredCertificate** | The APNS certificate for this application has expired. | Create a valid certificate using [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration#generating-a-certificate-request-file) and check that the platform is configured correctly. | | **FrequencyCapping** | The device was filtered by [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). | If such behavior was not intended, check the [Frequency Capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/) settings. | | **InvalidCertificate** | The APNS certificate for this application is invalid. | Create a valid certificate using [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration/#generating-a-certificate-request-file) and check that the platform is configured correctly. | | **InvalidProviderToken** | The specified token is invalid or signed with an invalid certificate. Notifications will no longer be sent to the platform where this error occurred. | Check that the platform is configured correctly. | | **InvalidToken** | The specified token is invalid. Notifications will no longer be sent to the platform where this error occurred. | Check that the platform is configured correctly.This error usually points to the mismatch between gateways (for example, if you’re sending pushes to your development/sandbox build through the production gateway or vice versa). Also, some of your users may have another build installed on their devices. | | **MissingDeviceToken** | The device token isn’t specified in the request. | Verify that the header contains the device token. | | **PayloadIsTooLarge** | The message size exceeds 4096 bytes. | Reduce the message size. | | **RevokedCertificate** | The APNS certificate for this application has been revoked via the Apple Developer admin panel. Push notifications will be disabled for this platform. | Renew your certificate using [this guide](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-platform-configuration/#generating-a-certificate-request-file). | | **SandboxSendingError** | Failed to send message to the sandbox APNS environment. | Try sending the message again later. | | **TimeoutExceeded** | Multiple attempts to send a message to the device within 1 hour were unsuccessful, as the APNS server did not respond. | Try sending the message again later. | | **Unregistered** | The token is no longer valid. The device will be unsubscribed from notifications. | Use a new token. | --- # SMS and messenger errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/ What each SMS and messenger error on the Message stats Errors tab means and how to fix it, from phone numbers to provider setup. --- This page lists the errors an SMS or messenger send can show on the **Errors** tab of [Message stats](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#errors-tab), and what to do about each one. Errors that every platform can show, such as **Phone Number Not Found** or **Skip Push**, are on [How to read the Errors tab](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform). ## How SMS and messenger errors appear on the Errors tab The **Errors** tab groups failed sends by platform. Each row shows an error name with its code in brackets, for example **Invalid To Phone Number (1001)**. ### Look up an error Find an error by its name and code together. The same number can stand for different errors, because Pushwoosh checks a message at several steps. For example, **Skip Push (1011)** and **Send Quota Exceeded (1011)** are different errors. Most errors come from your SMS provider or the messenger. Pushwoosh counts them in the **Provider error** category, whatever their cause. See [Error categories](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#error-categories) for what each category means. Tip When an error asks you to contact Support, include the message date and the **Message code**. To get the code, click **Message code** at the top of the message’s stats page. See [Copy the message code](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/sms-and-messenger-statistics/#copy-the-message-code). ### What the Errors tab doesn’t show The tab lists only errors returned at the moment Pushwoosh sends the message. These cases aren’t listed: * **Failures after the provider or messenger accepted the message.** For example, a failure at the mobile operator. The send counts as accepted. * **Some temporary failures.** These include most network errors. Pushwoosh retries them. On WhatsApp, a network error isn’t retried and shows as **Internal Service Error** (10006). * **Subscribers skipped without [marketing consent](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/).** Pushwoosh excludes them from the audience before sending, so they aren’t errors. ## SMS errors These errors come from your SMS provider or from the SMS settings of your project. Which errors you see depends on the provider. For example, Twilio reports an opt-out, an invalid number, and a sender not allowed for the country as **Bad Request (1006)**, not as the more specific errors below. ### Phone number issues These errors are caused by the recipient’s phone number, not by your provider setup. | Error | What it means | What to do | | ---------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Invalid To Phone Number** (1001) | The phone number can’t be read or has no country code. Pushwoosh unsubscribes the number from SMS. | Store phone numbers in international format with the country code, for example `+14155550100`, and register the number again. | | **Unsubscribed Recipient** (1002) | The person unsubscribed from your messages with their mobile operator, or the number is on your provider’s block list. After an operator opt-out, Pushwoosh unsubscribes the number from SMS and records the opt-out. | Don’t send marketing messages to this number. See [Opt-outs Pushwoosh records automatically](https://docs.pushwoosh.com/product/messaging-channels/sms-viber-marketing-consent/#opt-outs-pushwoosh-records-automatically). If the person didn’t opt out, check the block list in your provider’s dashboard. | ### Provider account and setup These errors mean your SMS provider rejected the request because of the account, the credentials, the sender, or the message. | Error | What it means | What to do | | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Bad Request** (1006) | The provider rejected the request. On Twilio, this also covers a person who opted out with their mobile operator, an invalid or non-mobile number, and a sender that isn’t allowed for the recipient’s country. | Check the recipient’s number and the sender for that country in your provider’s dashboard. If the person opted out, stop sending to them. | | **No Permission For Region** (1003) | The recipient’s country isn’t allowed for this project or provider, or the provider credentials or sender aren’t set up in SMS configuration. | Check the provider credentials and sender number in [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/). If they’re correct, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) to check which countries the project can send SMS to. | | **Invalid To From Combination** (1012) | Your sender ID isn’t allowed to send to the recipient’s country, or no sender ID is set in SMS configuration. | Check in your provider’s dashboard which sender to use for that country. | | **Permission Denied** (1015) | The provider didn’t accept your credentials. | Check the credentials in [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) against your provider’s dashboard. | | **Forbidden** (1007) | The provider refused the request or the connection, for example because of wrong credentials, or the SMS connection isn’t set up. | Check the credentials in [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/) against your provider’s dashboard. | | **Account Is Inactive** (1014) | Your account with the SMS provider is suspended. | Restore the account with your provider. | | **Not Found** (1008) | The provider couldn’t find the account or sender in the request. | Check the account and sender settings in your provider’s dashboard. | | **Invalid MMS Attachment** (1016) | The provider rejected the attached file because of its format or size. | Attach a smaller file in a format your provider supports. | | **Internal Service Error** (10006) | On SMS, it can also mean that SMS sending isn’t enabled for your account. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | ### Sending limits This error means the send hit a limit on the number of SMS, not a problem with the message. | Error | What it means | What to do | | ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- | | **Send Quota Exceeded** (1011) | The project reached its daily SMS limit, or the provider limited the send. Pushwoosh sets the daily SMS limit when you configure SMS for the project. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) about the daily SMS limit. Split large sends across several days. | ### Provider failures These errors mean the provider failed to process the message or returned a response Pushwoosh couldn’t read. | Error | What it means | What to do | | ---------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Unknown Error** (1000), **Converting Error** (1004), **Provider Unknown Error** (1009), **Provider Internal Error** (1010) | The provider returned an error Pushwoosh doesn’t recognize, or failed on its side. **Converting Error** can also mean the provider settings in SMS configuration are incomplete. | Compare the number of these errors with earlier sends of a similar size. If they grow or repeat on every send, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | ## Viber errors Besides the [errors every platform can show](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform), Viber sends can show these two. Pushwoosh sends Viber messages through MStat, using the MStat credentials from your SMS configuration. If a subscriber has no Viber contact in the project at all, the send fails with **Push Token Not Found** (1007), one of the [errors every platform can show](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/how-to-read-the-errors-tab/#errors-on-every-platform). | Error | What it means | What to do | | ------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **No Device For Push Token** (10002) | The subscriber’s Viber contact is saved without a phone number. | Register the subscriber’s number for Viber again. See [Register Viber contacts](https://docs.pushwoosh.com/product/messaging-channels/viber/register-viber-contacts/). | | **Internal Service Error** (10006) | The MStat credentials or account ID are missing, the message content is invalid, or MStat rejected the message. The **Errors** tab doesn’t show which. | Check that MStat is set up with valid credentials in [SMS configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/), and check the message content. If both are correct, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | ## WhatsApp errors Most WhatsApp errors come from Meta. Meta often accepts a message first and rejects it later, for example when the number isn’t on WhatsApp or Meta limits marketing messages to that person. Such a send counts as **Accepted by gateway** in the funnel, gets no delivery confirmation, and isn’t listed on the **Errors** tab. ### Messages outside the 24-hour window A free-form message, one sent without an approved template, reaches a person only within 24 hours of their last message to your business number. Outside that window, the send fails with one of these errors. | Error | What it means | What to do | | -------------------------------- | ------------------------------------------------------------------------------------ | ---------------------------------------------------- | | **Re Engagement Message** (1030) | The person hasn’t written to your business number in the last 24 hours. | Send an approved template instead of free-form text. | | **Processing Error** (1035) | Pushwoosh skipped a free-form message because the 24-hour window had already closed. | Send an approved template instead of free-form text. | ### Recipient errors These errors are caused by the recipient or by how often you message them. | Error | What it means | What to do | | ----------------------------------------- | -------------------------------------------------------------- | -------------------------------------------------- | | **Recipient Invalid Phone Number** (1011) | The recipient’s number is the same as your business number. | Remove your own business number from the audience. | | **Accounts Pair Rate Limit Hit** (1059) | You sent too many messages to the same person in a short time. | Space out messages to the same person. | ### Template and content errors These errors mean Meta rejected the template or the content of the message. | Error | What it means | What to do | | ----------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------- | | **Template Does Not Exist** (1037) | The template doesn’t exist in the selected language, or it isn’t approved yet. | Check the template and its languages in WhatsApp Manager, then pick an approved one in the preset. | | **Template Param Count Mismatch** (1036) | The number of variable values doesn’t match the number of variables in the template. | Fill in every variable of the template in the preset. | | **Template Parameter Format Mismatch** (1040) | A variable value has the wrong format. | Check the variable values against the template in WhatsApp Manager. | | **Template Hydrated Text Too Long** (1038) | The text is too long after the variables are filled in. | Shorten the variable values or the template text. | | **Template Format Character Policy Violated** (1039) | The template content violates a WhatsApp policy. | Edit the template in WhatsApp Manager and submit it for approval again. | | **Template Is Paused** (1041) | Meta paused the template because of low quality. | Use another approved template, or improve this one in WhatsApp Manager. | | **Template Is Disabled** (1042) | Meta paused the template too many times and disabled it permanently. | Create a new template and use it in the preset. | | **Request Invalid Parameter** (1010), **Request Missing Required Parameter** (1009), **Parameter Value Is Not Valid** (1020), **Generic User Error** (1053) | A value in the message is missing or invalid. | Check the preset: template, variables, buttons, and attachment. | | **Unsupported Message Type** (1032) | WhatsApp doesn’t support this type of message. | Change the content type in the preset. | | **Media Upload Error** (1034) | Meta couldn’t load the attached media. | Use a file at a public `https` URL in a format WhatsApp supports. | ### Account and quality errors These errors mean Meta restricted your WhatsApp Business Account or business number. Resolve them in WhatsApp Manager, as described in [How to fix a blocked or limited sender](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/#how-to-fix-a-blocked-or-limited-sender). | Error | What it means | What to do | | --------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | | **Temporarily Blocked For Policies Violations** (1005) | Meta blocked the account for violating its policies. | Check the account status in WhatsApp Manager. | | **Account Has Been Locked** (1017) | Meta locked the account for a policy violation, or couldn’t verify data in the request. | Check the account status in WhatsApp Manager. | | **Account Is Restricted From Messaging In This Country** (1016) | The account can’t send messages to users in this country. | Check the account restrictions in WhatsApp Manager. | | **Spam Rate Limit Hit** (1008) | Meta limits how many messages this number can send, because many earlier messages were blocked or reported as spam. | Review the content and audience of recent messages, then lower the volume. | | **Business Eligibility Payment Issue** (1028) | There is a problem with the payment method of the account. | Check the payment method in WhatsApp Manager. | | **Provided Number Needs Display Name Approval** (1027) | The display name of your business number isn’t approved yet. | Wait for approval or update the display name in WhatsApp Manager. | | **Business Phone Number Is Deleted** (1024) | Your business number was deleted. | Connect an active number in the [WhatsApp configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/). | | **Account In Maintenance Mode** (1035) | The account is in maintenance mode. This is a different error from **Processing Error** (1035) in [Messages outside the 24-hour window](https://docs.pushwoosh.com/product/troubleshooting/sending-errors/sms-and-messenger-errors/#messages-outside-the-24-hour-window). | Try again later. | ### Setup and permission errors These errors mean the connection between Pushwoosh and your WhatsApp Business Account doesn’t work. Reconnect your WhatsApp Business Account in **Settings > Configure Platforms > WhatsApp**, as described in [WhatsApp configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/). | Error | What it means | What to do | | ------------------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | **Access Token Expired** (1003) | The access token of the connection expired. | Reconnect the account. | | **Authentication Failed** (1002), **Permission Denied** (1013), **API Permission** (1014), **API Method** (1015), **Access Denied** (1023) | Meta didn’t accept the connection or a permission is missing. | Reconnect the account and grant all requested permissions. | | **Incorrect Certificate** (1029) | The business number isn’t registered correctly. | Check the business number in WhatsApp Manager, then reconnect the account. | | **Internal Service Error** (10006) | The business number or access token is missing from the WhatsApp setup, or Pushwoosh couldn’t reach Meta or read its reply. | Check the WhatsApp setup and reconnect the account if needed. If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | ### Rate limits These errors mean you sent faster than Meta allows for your app or account. | Error | What it means | What to do | | ----------------------------------------------------------- | -------------------------------------------------- | -------------------------------------------------------------- | | **Rate Limit Hit** (1058) | The message throughput of your number was reached. | Lower the sending rate or split the send into smaller batches. | | **API Too Many Calls** (1056), **Rate Limit Issues** (1057) | The app or the account reached its request limit. | Lower the sending rate or split the send into smaller batches. | ### Temporary Meta errors These errors mean Meta failed on its side or returned a response Pushwoosh doesn’t recognize. | Error | What it means | What to do | | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **API Unknown** (1018), **API Service** (1019), **Something Went Wrong** (1022), **Service Unavailable** (1025), **Server Temporary Unavailable** (1046) | A temporary error on Meta’s side. | Send the message again later. If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message date and **Message code**. | | **Unknown Error** (1000), **Cannot Process Sending** (1012) | Meta returned an error Pushwoosh doesn’t recognize, or a response it couldn’t read. | If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message date and **Message code**. | ## Telegram errors Telegram sends show two errors of their own. | Error | What it means | What to do | | ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------ | | **Chat Unavailable** (410007) | The person blocked or removed the bot, the chat wasn’t found, or the Telegram account was deactivated. Pushwoosh unsubscribes the person from Telegram. | Nothing to fix. The person can’t receive messages from the bot. | | **Invalid Authentication Info** (401002) | The bot token is missing or invalid. It also appears when a subscriber’s Telegram chat ID isn’t valid, and then Pushwoosh unsubscribes that subscriber, or when Pushwoosh couldn’t reach Telegram to check the token. | Check the bot token in [Telegram configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/telegram-configuration/). | ## Facebook Messenger errors These errors come from Meta when Pushwoosh sends a message from your Facebook Page. | Error | What it means | What to do | | ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Outside Allowed Window** (403004) | The person hasn’t written to your Page in the last 24 hours, and the message has no message tag. Pushwoosh doesn’t filter these people out of the audience, so each such send fails with this error. | Outside the 24-hour window, send with a message tag. See [Use a message tag outside the 24-hour window](https://docs.pushwoosh.com/product/messaging-channels/facebook-messenger/send-facebook-messenger-messages/#use-a-message-tag-outside-the-24-hour-window). | | **Recipient Unavailable** (410005) | The person can’t receive messages from your Page. Pushwoosh unsubscribes the person from Facebook Messenger. | Nothing to fix. | | **Invalid Authentication Info** (401002) | The Page ID or Page Access Token is missing, or Meta didn’t accept the token. | Enter a valid **Page ID** and **Page Access Token** in [Facebook Messenger configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/facebook-messenger-configuration/). | | **No Permission** (403003) | Your Meta App doesn’t have the permission this send needs. | Check the Messenger permissions of your Meta App in the Meta App dashboard. | | **Invalid Request Parameter** (400001) | The message is empty or contains an invalid value. | Check the message content. | | **Internal Server Error** (5009999) | Meta returned an error Pushwoosh doesn’t recognize. | If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message date and **Message code**. | ## Kakao errors Most Kakao errors come from the KakaoTalk messaging service. Pushwoosh shows each one under its own name, grouped below by what to check. | Error | What it means | What to do | | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Missing User Code** (4001200), **Missing Secret Key** (4001201) | The KakaoTalk credentials aren’t filled in. | Enter the **USER CODE** and **SECRET KEY** in [Kakao configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration/). | | **Invalid Authentication Info** (401002), **Invalid Token** (401005), **Token Expired** (401006), **Missing Authorization Header** (401004), **No Permission** (403003), **Unregistered Account** (4001100) | The service didn’t accept the access token or your account. | Check the **USER CODE** and **SECRET KEY** in [Kakao configuration](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/kakao-configuration/). If they’re correct, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **Unauthorized IP Address** (4031101) | The service doesn’t accept requests from the address Pushwoosh sends from. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **Insufficient Balance** (4001102) | Your KakaoTalk messaging account has no balance left. | Top up the balance of your KakaoTalk messaging account. | | **Unit Price Not Found** (5001103) | The service has no unit price for this message on your account. | Contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **Unauthorized Sender Key** (4001104), **Missing Sender Key** (4001301), **Missing Sender Number** (4001208), **Invalid Sender Number Not Registered** (400667), **Invalid Sender Number Policy Violation** (400668) | The sender profile or sender number isn’t registered or isn’t allowed. | Check the sender profile and sender number in your KakaoTalk messaging account. | | **Template Code Not Found** (4001105), **Missing Template Code** (4001303) | The template code in the message is missing or doesn’t match a registered template. | Check that the message uses an approved [Kakao template](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/kakao-presets/). | | **Recipient Opted Out** (400090) | The person opted out of your KakaoTalk messages. | Don’t send marketing messages to this person. | | **Missing Recipient Number** (4001202), **Invalid Recipient Number Format** (4001204), **Recipient Number Too Long** (4001203), **Missing Country Code** (4001210), **Invalid Country Code Format** (4001212) | The recipient’s phone number is missing or has the wrong format. | Store phone numbers in international format with the country code and register the number again. | | **Missing Message Body** (4001206), **Message Body Too Long** (4001207), **Title Too Long** (4001205), **Header Too Long** (4001306), **Invalid Request Parameter** (400001), and other errors named **… Too Long** or **Invalid …** | A part of the message is missing, too long, or has an invalid value. | Fix the field the error names in the KakaoTalk preset or message. | | **Internal Server Error** (5009999) | The service failed on its side or returned an error Pushwoosh doesn’t recognize. | If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new) with the message date and **Message code**. | ## LINE errors Most LINE errors follow the HTTP status the LINE Messaging API returned. | Error | What it means | What to do | | ---------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **Unable To Get Channel ID** (1011), **Unable To Get Channel Secret** (1012) | The **Channel ID** or **Channel secret** isn’t filled in. | Enter both in the [LINE configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/line-configuration/). | | **Authentication Failed** (1003) | LINE didn’t accept the access token for the channel, for example because the channel secret was changed. | Check the **Channel ID** and **Channel secret** against the LINE Developers Console. | | **Duplicate Post** (1006) | LINE refused the request, either as a duplicate or because the channel isn’t allowed to use this part of the Messaging API. | Check the plan and permissions of your LINE Official Account. If they’re correct, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | | **Profile Not Found** (1005), **Access Not Available** (1007) | The recipient or the requested resource is no longer available on LINE. | Nothing to fix for this recipient. | | **Bad Request** (1002) | LINE rejected the request. | Check the message content and the LINE preset. | | **Payload Too Large** (1008) | The message is larger than LINE allows. | Make the message or its media smaller. | | **Unsupported Media Type** (1009) | LINE doesn’t support the type of the attached file. | Attach the file in a format LINE supports. | | **Too Many Requests** (1010) | The channel reached the LINE request rate limit or its monthly message limit. | Lower the sending rate, or check the message limit of your LINE Official Account plan. | | **Internal Server Error** (1001), **Unknown Error** (1000) | LINE failed on its side or returned an error Pushwoosh doesn’t recognize. | Send the message again later. If the error repeats, contact [Pushwoosh Support](https://help.pushwoosh.com/hc/en-us/requests/new). | --- # Windows errors **URL:** https://docs.pushwoosh.com/product/troubleshooting/sending-errors/windows-errors/ Keep your push campaigns on track by learning how to handle Windows platform errors that may impact notification delivery. --- These errors come from Windows Notification Services (WNS) when Pushwoosh sends push notifications to Windows devices. | Error | Description | What to do | | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | **BadChannel** | The push token (URI) is invalid or not recognized by WNS. | Check the push token and try again. | | **BadRequest** | The following reasons can cause the error:- An invalid value was specified in the *wns_content* field (it is empty or contains incorrectly encoded XML). - An HTTP request to the WNS service failed. | Check the *wns_content* field value in the request. | | **FailedToGetWNSToken** | The following reasons can cause the error:- WNS is currently unavailable (this reason is more common). - WNS authentication failed due to invalid credentials. | Сheck the credentials and try again later. | | **InvalidToken** | The push token is invalid; the device will be unsubscribed from notifications. | Use the valid token. | | **Overload** | WNS push limit exceeded: WNS can’t handle the high number of pushes sent. | Try reducing the number of pushes you send. | | **PayloadTooLarge** | The message size exceeds the 5000-byte limit. | Reduce the message size. | | **TokenForWrongChannel** | The push token is invalid for the credentials used to send the push. | * Ensure that the package name in your app’s manifest matches the credentials specified in the Pushwoosh Control Panel when configuring the platform. * Double-check whether the access token provided in the request matches the credentials of the app that requested the channel URI. | | **Unregistered** | The push token is invalid; the device will be unsubscribed from notifications. | Try using a new token. | | **WNSFailed** | An internal WNS failure caused notification delivery to fail. | Try sending the message again later. | | **WNSUnavailable** | Multiple attempts to send a message to the device within 10 minutes were unsuccessful, as the WNS server did not respond. | Try sending the message again later. | --- # Industry playbooks **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/ Step-by-step guides by industry that set up user data, build segments, and launch the first journeys and campaigns in Pushwoosh. --- Start sending the right messages to the right people without piecing the setup together from separate feature docs. An industry playbook shows what a business like yours needs in Pushwoosh, in what order to set it up, and which campaigns to launch first. Each playbook takes you from the first setup steps to your first running campaigns. Follow its sections in order. [Mobile games ](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/) [Onboarding, retention, and monetization journeys for mobile games.](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/) --- # Mobile games **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/ Set up player identification, events, segments, and journeys for a mobile game in Pushwoosh, from the first install to win-back. --- This playbook shows how to set up Pushwoosh for a mobile game, from identifying players to launching journeys for onboarding, engagement, reactivation, and in-app purchases. Set up Pushwoosh in seven stages, in this order: 1. Set a [User ID](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id) for each player, so Pushwoosh recognizes the same player on every device. 2. Choose the [events to track](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#set-up-the-events-to-track) to record what players do in the game. 3. Optional: store player data, such as the level or whether the player has paid, in [tags](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#store-player-data-in-tags). 4. Group players who match your conditions in [segments](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/), for example players who have not opened the game for 30 days. 5. Ask for push permission at the right moment, set delivery rules, and set up a control group. See [Prepare for communication](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/). 6. Send messages automatically with [journeys](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/) when a player does something in the game or joins a segment. 7. [Test each journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/), then read its statistics and measure results. To start with less, follow [Minimum setup to start messaging players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/#minimum-setup-to-start-messaging-players). ## Prerequisites Developer assistance needed Some steps of this playbook are changes in your game’s code. See [What your developers need to do](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/#what-your-developers-need-to-do). * An [application in your Pushwoosh account](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/create-your-project/), with the [SDK integrated](https://docs.pushwoosh.com/developer/first-steps/integrate-pushwoosh-sdk/) so devices register automatically. * Email and App Inbox only for the journeys that list them under **Needs**: [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) uses email, and [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) uses App Inbox. Most journeys in this playbook use push and in-app messages. ## What your developers need to do Hand your developers this list, so they can ship it in one release: * [Set the User ID at login](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id). * [Send the events](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#send-the-events-from-the-game) your journeys need. * [Track purchases](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#track-revenue-as-pw_conversion) and, if you sell subscriptions, [pass the player’s ID with each store purchase](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#track-subscriptions-from-the-stores). * [Move the push permission request](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#ask-for-push-permission-at-the-right-moment) out of the first launch. * Handle [deep links](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) to the level, reward, store, and event screens, which the journeys open from pushes. * Integrate the Message Inbox SDK if you use [App Inbox](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements). ## Minimum setup to start messaging players If you do not need all seven stages yet, this minimum setup covers two basic tasks: getting new players through the tutorial and winning back players who stopped playing. Complete these steps: 1. Ask your developers to call `setUserId` on login. See [Identify players by User ID](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id). 2. Check that `PW_ApplicationOpen` arrives from the game. Ask your developers to also send the `Tutorial started` and `Tutorial completed` events. See [Set up the events to track](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#set-up-the-events-to-track) and [Send the events from the game](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#send-the-events-from-the-game). 3. Move the push permission request to a later moment, for example after the tutorial. On iOS, a player who declines the prompt on the first launch can’t be asked again. See [Ask for push permission at the right moment](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#ask-for-push-permission-at-the-right-moment). 4. Set the [delivery rules](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-delivery-rules-before-you-message-players). 5. Build the [At-risk players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#at-risk-players) segment. If you already track purchases with `PW_InAppPurchase`, also build [Recent payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#recent-payers). 6. Create the content for these two journeys, as listed under **Content to prepare** in each recipe. 7. Launch [Onboarding for new players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#onboarding-for-new-players) and [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players). Add the other segments and journeys later, as their events start arriving. ## What next To add more journeys, see these guides: * [Set up a retention campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/) * [Set up a user engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh/) * [Create a re-engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh/) * [Run A/B/n tests in journeys](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-abn-test-customer-journeys/) --- # Build the journeys **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/ Journey recipes for a mobile game in Pushwoosh that cover onboarding, engagement, reactivation, in-app purchases, and live events. --- Each recipe on the pages of this section is one journey. The journeys and their message texts are examples: build the ones you need and replace the texts with your game’s tone and rewards. To open the canvas, see [Start creating your first journey](https://docs.pushwoosh.com/product/customer-journey/getting-started/#start-creating-your-first-journey). For the canvas basics, see the [Customer Journey overview](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). Let AI Assistant create the journeys [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) can create the journeys from these recipes in your application. Send it the link to the recipe page and name the journey you need. A journey it creates starts as a draft and doesn’t affect players until you launch it. Check each change before you approve it. Players in many games move through similar stages. Use this table to pick the journeys for the stage where you lose players. | Stage | What is happening | Journeys | | ------- | ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | New | The player installed the game and is learning it | * [Onboarding for new players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#onboarding-for-new-players) * [Second session for new players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#second-session-for-new-players) | | Playing | The player plays and progresses | - [Help for stuck players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#help-for-stuck-players) - [Daily reward reminder](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#daily-reward-reminder) | | Buying | The player is ready to spend, or has paid | * [First purchase offer](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/#first-purchase-offer-for-engaged-non-payers) * [Second purchase offer](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/#second-purchase-offer) * [Store abandonment reminder](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/#store-abandonment-reminder) | | Loyal | The player stays for the long run | - [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) - [Anniversary reward](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#anniversary-reward) | | Leaving | The player stopped opening the game | * [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) | ## Prepare to build a journey A message element in a journey uses ready content, so create the content first in **Content**: [push presets](https://docs.pushwoosh.com/product/content/push-presets/), [in-app templates](https://docs.pushwoosh.com/product/content/in-apps/), [email content](https://docs.pushwoosh.com/product/content/email-content/), or [App Inbox presets](https://docs.pushwoosh.com/product/content/app-inbox-presets/). Each recipe lists the content it needs. To personalize the content: * **Player data:** to put a value stored on the player into a push, such as the `player_level` tag, add a [Dynamic content](https://docs.pushwoosh.com/product/personalization/dynamic-content/) placeholder to the text. * **Language:** to send each player the message in their own language, add the languages to the push or in-app content. Pushwoosh picks the version by the device’s Language tag, which the SDK sets from the OS locale, so you do not need a custom language tag. See [Multi-language messaging](https://docs.pushwoosh.com/product/personalization/multi-language/). ## Best practices for every journey Apply these to every journey in this playbook: | Practice | Why | | --------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Set a silence period | A push should not reach a player in the middle of the night. The period blocks messages in each player’s device timezone, for example 23:00 to 08:00. Learn more: [Silence period](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#silence-period) | | Limit how often a player enters | A repeating event such as `Level completed` starts the journey every time, so one player could get the same message again and again. Learn more: [Campaign entry limit](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) | | Choose the message type on every message element | A journey that starts on an event does not preselect the type, and the element shows **Message type not selected** until you choose one. Learn more: [No preselected message type](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#no-preselected-message-type) | | Use Marketing message for offers, reminders, and win-back | A marketing message follows the limits you set: [frequency capping](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-delivery-rules-before-you-message-players), silence periods, and [control groups](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-up-a-control-group-before-you-launch). Transactional messages skip frequency capping and control groups, but not silence periods, so keep them for service messages. Learn more: [Choosing the right message type](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/#choosing-the-right-message-type) | | Add a conversion goal | The conversion rate of the goal in **Performance** shows how many players did the action you chose. Learn more: [Conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), [Measure results](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/#measure-results) | | Plan for players without push | A player who has not allowed notifications gets no push. Add a Reachability check and send an in-app message or an email instead. Learn more: [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/), [Ask for push permission at the right moment](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#ask-for-push-permission-at-the-right-moment) | | Check who a segment journey takes in | A journey that starts from a segment takes in only devices with a push token. The token arrives when the game asks for push permission, even if the player declines, so these players enter and get the in-app message. Players the game has not asked yet do not enter | | Set an expiration on in-app messages | A journey in-app usually appears the next time the player opens or returns to the game, so set **In-app expiration**, for example 1 day, to keep old messages from appearing days later. To reach players outside the game, use a push. Learn more: [In-app expiration](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/#select-template-and-set-expiration) | | Test every journey before you launch it | A journey has no test mode, so once it is running, real players who match its entry get its messages. Learn more: [Test each journey before you launch it](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/#test-each-journey-before-you-launch-it) | | Set delivery rules before you launch | They limit how often a player hears from you across all journeys. Learn more: [Set delivery rules before you message players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-delivery-rules-before-you-message-players) | --- # Loyalty and win-back **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/ Journey recipes for live events, install anniversaries, and winning back mobile game players who stopped opening the game. --- These recipes cover the Loyal and Leaving stages: players who stay for the long run and players who stopped opening the game. Before you build them, read the [best practices for every journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/#best-practices-for-every-journey). ## Live event and VIP announcements Developer assistance needed The App Inbox card layouts need Android SDK 6.12.0 with `pushwoosh-inbox-ui`, or iOS SDK 7.0.40 and later with PushwooshInboxKit. Carousel needs iOS SDK 7.1.2. Older versions show the message without the layout. * **Use it when:** you run a live event, such as a tournament, or give some players early access. * **Starts when:** the date and time you set, for the segment you pick. * **What it does:** announces the event in App Inbox and by push when it starts, then sends a reminder push shortly before it ends. The announcement goes to the players most likely to care. * **Content to prepare:** an App Inbox preset with the announcement, and two push presets: one for the start and one for the reminder before the end. * **Needs:** the Message Inbox SDK in the game for App Inbox. The Pushwoosh Unity plugin has no Message Inbox, so a Unity game can’t show the App Inbox message. ![Live event journey: Active players entry, App Inbox announcement, start push, delay until near the end, last-chance push](https://docs.pushwoosh.com/industry-playbooks-mobile-games-7.webp) 1. On the journey canvas, add an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) on the [Active players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#active-players) segment. For an exclusive-content event, use [Achievement hunters](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#achievement-hunters). For early access, use [Frequent payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#frequent-payers). 2. Set the entry schedule to **One-time entry**, choose **Schedule for a specific day**, and set the launch date, time, and timezone. 3. Add an [App Inbox](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/app-inbox/) element with the event announcement. Set its expiration to match the event end. 4. Add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that links to the event screen, for example: “The weekend tournament has started. Join now.” 5. Add a [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/), choose the **Date** type, and set a moment shortly before the event ends. The **Date** type counts in each player’s own time zone. If the event ends at the same moment for everyone, choose **Fixed duration** from the start instead. 6. Add a push reminder, for example: “The tournament ends in 3 hours. Last chance to join.” 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `Live event joined`, and select **End journey after this event**, so players who already joined do not get the reminder. More ways to run a live event * **Tournament:** keep the event in view outside the game with a [Live Activity](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/) element. Start the card when the event opens, update it during the event, and end it when the event closes. It works on iOS and on Android 16 and later. * **Event content:** to change it without an app update, send the new configuration with a [Data to app](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/data-to-app/) element, which delivers it as a silent push. Your developers decide what the game does with the data. * **One announcement:** for a single announcement with no follow-up, such as a game update or a seasonal sale, send a [one-time push](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/) instead of building a journey. ## Anniversary reward * **Use it when:** you want to celebrate how long a player has been with the game and give them a reason to come back. * **Starts when:** a player enters the [Install anniversary](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#install-anniversary) segment, on the anniversary of their install date. Pushwoosh checks the segment on a schedule you set, for example daily. * **What it does:** sends a push with an anniversary gift, or an in-app message to players who can’t get push. * **Content to prepare:** a push preset and an in-app template with the anniversary gift. * **Needs:** no extra setup. `First Install` is a default tag that Pushwoosh sets on every device. ![Anniversary reward journey: Install anniversary entry repeated every day, Reachability check, then push or in-app](https://docs.pushwoosh.com/industry-playbooks-mobile-games-9.webp) 1. On the journey canvas, add an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) with the [Install anniversary](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#install-anniversary) segment. 2. Set the entry schedule to **Recurring entry**, choose **Periodically**, and set it to daily. The segment matches only on the anniversary day, so the journey must check it every day. 3. Add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 4. For players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) with the gift, for example: “One year together! Your anniversary chest is waiting.” 5. For the others, add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) with the same gift and set **In-app expiration** to a few days, so the player still sees it after the day itself. 6. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once a month or once in 3 months, so a player gets one message per anniversary. Once in a lifetime would also block the next year’s anniversary. 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_ApplicationOpen`, and select **End journey after this event**. Other dates to greet players * **Before the day:** build the segment with **is in N days** instead of **is today**. * **Other milestones:** for a milestone that is not a yearly date, such as 100 days since install, filter `First Install` with **exactly N days ago** instead of an anniversary operator. ## Reactivation for inactive players * **Use it when:** players stop opening the game. * **Starts when:** a player enters the [At-risk players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#at-risk-players) segment, after a week without a session. * **What it does:** sends a push with an offer, different for payers and for other players. If the player does not open the game within 3 days, a second push with a time-limited reward follows, or an email for players who cannot get push. * **Content to prepare:** two push presets for the first message (one for payers, one for other players), a push preset with the comeback reward, and email content for players you cannot reach by push. * **Needs:** for the email step, the email channel set up in your application and email addresses stored in your players’ profiles. ![Reactivation journey: At-risk entry, split on Recent payers, two pushes, then push or email by reachability](https://docs.pushwoosh.com/industry-playbooks-mobile-games-4.webp) 1. On the journey canvas, add an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) on the [At-risk players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#at-risk-players) segment. 2. Set the entry schedule to **Recurring entry** and choose **Periodically**, for example daily. Pushwoosh recalculates the segment before each start, and players already in the journey do not enter again. 3. Add a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/), choose **Segment**, and select the [Recent payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#recent-payers) segment. If you do not track purchases yet, skip the split and send every player the push from step 5. 4. On the **Users in a segment** branch, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) with an offer tied to how the player spends, for example: “Your next pack comes with a bonus. It is waiting in the store.” 5. On the **All other users** branch, add a push with a softer reason to return, such as new content or a comeback bonus, for example: “Your hero is waiting at level 12. Come back and keep going.” 6. In each branch, add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `PW_ApplicationOpen` and set the waiting period to 3 days. 7. On each **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 8. For players reachable by push, send a second push with a time-limited comeback reward, for example: “We saved a gift for you. Claim it in the next 48 hours.” 9. For the others, send an [email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/). Only players with an email address in their profile receive it. 10. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_ApplicationOpen`, and select **End journey after this event**. 11. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once a month. A player stays in At-risk players for up to 3 weeks, so without the limit the daily entry would send the same pushes again after each round. Players inactive for 30 days or more To cover players inactive for 30 days or more, clone the journey, set its entry to the [Lapsed players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#lapsed-players) segment, and replace the push with a bigger comeback offer. A player can stay in Lapsed players for up to a year, so set the clone’s [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit) to once in 3 months. See [Clone a journey](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#clone). --- # New and active players **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/ Journey recipes that help new mobile game players through their first days and keep active players coming back every day. --- These recipes cover the New and Playing stages: players who are learning the game and players who play every day. Before you build them, read the [best practices for every journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/#best-practices-for-every-journey). ## Onboarding for new players * **Use it when:** new players install the game but do not finish the tutorial. * **Starts when:** a player starts the tutorial (`Tutorial started`). * **What it does:** sends an in-app nudge 15 minutes after the tutorial starts, then a push after 24 hours, until the player finishes the tutorial. The first nudge is an in-app message that comes early, while the player may still be in the game, because most players have not allowed notifications yet. * **Content to prepare:** an in-app template and a push preset, both pointing the player back to the tutorial. ![Onboarding journey: Tutorial started entry, two Wait for Trigger steps on Tutorial completed, an in-app nudge, a push](https://docs.pushwoosh.com/industry-playbooks-mobile-games-1.webp) 1. On the journey canvas, add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on `Tutorial started`. Do not use `PW_DeviceRegistered` here: it fires when the game asks for push permission, which this playbook moves to after the tutorial. 2. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `Tutorial completed` with the condition `completion` is true, and set the waiting period to 15 minutes. 3. Leave the **Trigger group 1** branch empty, because players who finish the tutorial need no further messages. 4. On the **Not triggered** branch, add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) with a message that points back to the tutorial, for example: “Your first reward is one step away. Finish the tutorial to claim it.” The player sees it the next time they open or return to the game. 5. Add a second [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `Tutorial completed` with the condition `completion` is true, and set the waiting period to 24 hours. Leave its **Trigger group 1** branch empty too. 6. On the **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 7. For players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that sends them back to the tutorial, for example: “Your adventure is waiting. Finish the tutorial to unlock your first reward.” 8. Open **Settings** on the canvas, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `Tutorial completed` with `completion` is true, and select **End journey after this event**. 9. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once in a lifetime, so a player who restarts the tutorial does not get the nudges again. The push in step 7 reaches only players whose devices already allow notifications. The others leave the journey, because a second in-app would repeat the first one. This journey starts only for players who start the tutorial after you launch it. To reach players who started earlier, build a journey with a one-time Audience-based entry on the [Tutorial drop-off](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#tutorial-drop-off) segment. It takes in only players whose devices already have a push token, which the game gets when it asks for push permission. No tutorial in your game? If your game has no tutorial, see [Choose the events to track](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#choose-the-events-to-track) for the events to use instead of `Tutorial started` and `Tutorial completed`. For a longer walkthrough of onboarding journeys, see [Onboard users with Pushwoosh](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/). ## Second session for new players * **Use it when:** new players finish the tutorial but do not come back the next day. * **Starts when:** a player finishes the tutorial (`Tutorial completed`). * **What it does:** waits about a day after the tutorial and, if the player has not opened the game again, sends a push that gives a reason to come back. * **Content to prepare:** a push preset with a reason to return, such as a reward waiting in the game. * **Needs:** players who allowed notifications during their first session. If the game asks for permission only at the next launch, this journey reaches few new players. ![Second session journey: Tutorial completed entry, 20-hour Wait for Trigger on PW_ApplicationOpen, push for reachable players](https://docs.pushwoosh.com/industry-playbooks-mobile-games-10.webp) 1. On the journey canvas, add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on `Tutorial completed` with the condition `completion` is true. 2. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `PW_ApplicationOpen` and set the waiting period to 20 hours. Leave its **Trigger group 1** branch empty, because players who came back need no message. 3. On the **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 4. For players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/), for example: “Your daily chest is ready. Come back and open it.” 5. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_ApplicationOpen`, and select **End journey after this event**. 6. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once in a lifetime. ## Help for stuck players * **Use it when:** players keep failing levels and may quit the game. * **Starts when:** a player enters the [Stuck players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#stuck-players) segment. Pushwoosh checks the segment on a schedule you set, for example daily. * **What it does:** shows an in-app hint or gift, then sends a push that offers help if the player does not complete the level within 2 days. The hint reaches the player before they quit, and the push is a second touch for players who have left the game. * **Content to prepare:** an in-app template with a hint or a gift, and a push preset that offers help. ![Stuck players journey: daily audience entry, in-app boost, Wait for Trigger on Level completed for 2 days, fallback push](https://docs.pushwoosh.com/industry-playbooks-mobile-games-2.webp) 1. On the journey canvas, add an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) with the [Stuck players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#stuck-players) segment. 2. Set the entry schedule to **Recurring entry** and choose **Periodically**, for example daily. 3. Add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) with a hint or a gift for the level, for example: “Stuck on this one? Here is a free boost to get you through.” The player sees it the next time they open or return to the game. 4. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) on `Level completed` for 2 days. 5. On the **Not triggered** branch, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that offers help and sets a deep link to the level, for example: “Need a hand? Your boost is ready. Tap to try the level again.” 6. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once a week. 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `Level completed`, and select **End journey after this event**. Players without push get only the in-app message, because a second in-app would repeat the same boost. ## Daily reward reminder * **Use it when:** your game has a daily reward and you want players to collect it every day. * **Starts when:** a player claims the reward (`Daily reward claimed`). * **What it does:** waits for your reward cooldown after each claim, then reminds the player to collect the reward, unless the player has already claimed it. The reminder counts from the latest claim. * **Content to prepare:** a push preset and an in-app template with the same reminder. ![Daily reward journey: claim entry, 24-hour delay, Wait for Trigger on the next claim, then push or in-app by reachability](https://docs.pushwoosh.com/industry-playbooks-mobile-games-3.webp) 1. On the journey canvas, add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on `Daily reward claimed` and set the entry to **Allow re-entry**. Each claim restarts the journey, so the reminder always counts from the latest claim. 2. Add a [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) with a fixed duration equal to your reward cooldown, for example 24 hours. 3. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `Daily reward claimed` and set the waiting period to a few hours. The reward can’t be claimed before the cooldown ends, so the step catches every claim after the delay, and players who claim skip the reminder. 4. On the **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 5. For players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that tells the player the reward is ready, for example: “Your daily reward is waiting for you.” Set a [deep link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) so the push opens the reward screen. 6. For the others, add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) with the same reminder and set **In-app expiration** to 1 day. The player sees it the next time they open or return to the game. 7. Set a [Silence period](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#silence-period) for push with the option to send the message after the period ends. 8. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), and choose `Daily reward claimed`. --- # Purchases **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/ Journey recipes that turn engaged mobile game players into first-time payers and bring back players who left the in-game store. --- These recipes cover the Buying stage: players who are ready to spend or have already paid. Before you build them, read the [best practices for every journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/#best-practices-for-every-journey). ## First purchase offer for engaged non-payers * **Use it when:** players are invested in the game but have never paid. * **Starts when:** a player enters the [Engaged non-payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#engaged-non-payers) segment. Pushwoosh checks the segment on a schedule you set, for example weekly. * **What it does:** shows an in-app starter pack offer, then sends a push reminder after 1 day if the player does not buy. The segment requires 10 completed levels in 14 days, so only invested players get the offer and new installs do not. * **Content to prepare:** an in-app template with the starter pack offer and a push preset with the reminder. * **Needs:** `PW_InAppPurchase` set up. Without it, the segment also matches players who have paid, and the reminder push goes to buyers too. ![First purchase journey: Engaged non-payers entry, starter pack in-app, Wait for Trigger on purchase for 1 day, reminder push](https://docs.pushwoosh.com/industry-playbooks-mobile-games-5.webp) 1. On the journey canvas, add an [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) on the [Engaged non-payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#engaged-non-payers) segment. 2. Set the entry schedule to **Recurring entry** and choose **Periodically**, for example weekly. 3. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once a month, so a player who ignores the offer does not get it every week. 4. Add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) that offers a starter currency pack, for example: “Starter pack: 500 coins for a special price.” The message appears when the player opens the game. 5. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `PW_InAppPurchase` and set the waiting period to 1 day. Leave the **Trigger group 1** branch empty. 6. On the **Not triggered** branch, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that mentions the offer, for example: “Your starter pack is still available.” It reaches both the players who saw the in-app and did not buy, and the players who have not opened the game since. 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_InAppPurchase`, and select **End journey after this event**. Players without push see only the in-app offer, because a second in-app would repeat it. ## Second purchase offer * **Use it when:** you want players who paid once to buy again. * **Starts when:** a player makes a purchase (`PW_InAppPurchase`). * **What it does:** checks whether this is the player’s first purchase. For first-time payers, it sends a thank-you in-app a few minutes after the purchase and a push with a second-purchase offer 3 days later, unless they buy again before that. * **Content to prepare:** an in-app template with the thank-you and a push preset with the offer. * **Needs:** `PW_InAppPurchase` set up and the [First-time payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#first-time-payers) segment. ![Second purchase journey: purchase entry, 5-minute delay, split on First-time payers, thank-you in-app, purchase wait, push](https://docs.pushwoosh.com/industry-playbooks-mobile-games-8.webp) 1. On the journey canvas, add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on `PW_InAppPurchase`. 2. Add a [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) with a fixed duration of 5 minutes. Segments count a new event only about a minute after it arrives, so without the delay the split can miss the first purchase. 3. Add a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/), choose **Segment**, and select the [First-time payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#first-time-payers) segment. Leave the **All other users** branch empty, because repeat payers need no message. 4. On the **Users in a segment** branch, add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) that thanks the player, for example: “Thanks for your support! Here is a bonus for your next run.” Set **In-app expiration** to 1 day. 5. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `PW_InAppPurchase` and set the waiting period to 3 days. Leave its **Trigger group 1** branch empty. 6. On the **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/), and for players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) with the offer, for example: “A special pack is waiting for you in the store.” 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_InAppPurchase`, and select **End journey after this event**. ## Store abandonment reminder * **Use it when:** players open the in-game store and leave without buying. * **Starts when:** a player opens the store (`Store opened`). * **What it does:** waits 1 hour after the player opens the store, then sends a push or in-app reminder if the player has not bought anything. Players who buy do not get the reminder. * **Content to prepare:** a push preset and an in-app template with the same reminder. * **Needs:** `PW_InAppPurchase` set up, because the journey checks for a purchase after the visit. ![Store abandonment journey: Store opened entry, Wait for Trigger on purchase for 1 hour, then push or in-app by reachability](https://docs.pushwoosh.com/industry-playbooks-mobile-games-6.webp) 1. On the journey canvas, add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) on `Store opened`. 2. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step on `PW_InAppPurchase` and set the waiting period to 1 hour. Leave the **Trigger group 1** branch empty. 3. On the **Not triggered** branch, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/). 4. For players reachable by push, add a [push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) that sets a deep link to the store, for example: “Still thinking it over? Your pack is waiting in the store.” 5. For the others, add an [In-app element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) with the same reminder and set **In-app expiration** to 1 day. The player sees it the next time they open or return to the game. 6. Open **Settings**, select [**Campaign Entry Limit**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#campaign-entry-limit), and choose once a week, so a player who browses the store every day does not get a reminder every day. 7. Open **Settings**, select [**Conversion goals**](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals), choose `PW_InAppPurchase`, and select **End journey after this event**. --- # Build the segments **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/ Build the segments that the journeys of a mobile game use in Pushwoosh, from players who stopped playing to payers and VIPs. --- A [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) is a saved group of players who match your conditions, for example players who have not opened the game for 30 days. You can send a one-time push, in-app message, or email to a segment, or use it in a journey to decide who enters and which message each player gets. Build the segments before the journeys. ## How to create segments Create each segment in **Audience** → **Segments** with the filters from the tables below. There are several ways to build a segment. This page covers segments by events and by tags. You can also build segments from: * [Existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) * [Anniversary dates](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/anniversary-segments/) * [User ID](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-user-id/) * [An imported file](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-import/) * [Geolocation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-geo/) * [A description in plain words, with AI](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/build-segments-with-ai/) For all the ways, see [How to create segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/). Let AI Assistant create the segments [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) can create the segments from this page in your application. Send it the link to the page and ask it to create them. Check each change before you approve it. Inactivity segments need event history: At-risk players start to fill 8 days after `PW_ApplicationOpen` events begin to arrive, and Lapsed players after 30 days. Ready-made activity or spend tiers RFM segmentation groups players by how recently and how often they act, and builds the segments for you, such as At risk and Can’t lose them. To build them: 1. Open **Audience** → **RFM Segmentation**. 2. Select `PW_ApplicationOpen` as the **Purchase Event**. 3. Leave **Monetary Attribute** empty. 4. Click **Calculate**. To group payers by spend instead, select `PW_Conversion` as the **Purchase Event** and its `value` attribute as the **Monetary Attribute**. Segments such as Champions then hold the players who buy most often and most recently, and each segment shows the average spend of its players. An application keeps one RFM setup, so the spend tiers replace the activity tiers. Players who have never paid are not in these segments. Pushwoosh builds 10 segments, refreshes them every 24 hours, and scores only the last 90 days. See [RFM segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/). ## Segments by events These segments are built from the main events that players trigger in the game. Use them as a starting point or for inspiration: change the numbers, such as the days, or build your own segments. To set the filters, see [Build segments by events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/). Most of them feed a journey from [Build the journeys](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/), so build the ones for the journeys you plan to launch. | Segment | Filters | Use it to | | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Tutorial drop-off | * `Tutorial started` exactly or more, 1 time, during the last 7 days * AND `Tutorial completed` never, during the last 7 days | Message players who started the tutorial in the last 7 days and have not finished it. Feeds a one-time entry of the [Onboarding for new players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#onboarding-for-new-players) journey | | Active players | - `PW_ApplicationOpen` exactly or more, 1 time, during the last 7 days | Send announcements to players who still play, for example about a new event. Feeds the [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) journey | | At-risk players | * `PW_ApplicationOpen` never, during the last 7 days * AND `PW_ApplicationOpen` exactly or more, 1 time, during the last 30 days | Win back players who stopped opening the game 8 to 30 days ago. This is the first stage of reactivation. Feeds the [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) journey | | Lapsed players | - `PW_ApplicationOpen` never, during the last 30 days - AND `PW_ApplicationOpen` exactly or more, 1 time, during the last 365 days | Win back players who have not opened the game for 30 days, with a bigger comeback offer than for At-risk players. The second filter leaves out devices with no app opens recorded at all. Feeds a copy of the [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) journey | | Engaged non-payers | * `Level completed` exactly or more, 10 times, during the last 14 days * AND `PW_InAppPurchase` never, on or after the date you started tracking purchases | Offer a first purchase to players who play a lot but have never bought anything. Feeds the [first purchase offer](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/#first-purchase-offer-for-engaged-non-payers). Needs `PW_InAppPurchase` tracking: without it, the segment also matches players who have paid | | Frequent payers | - `PW_InAppPurchase` exactly or more, 3 times, during the last 30 days | Give the players who buy most often their own offers, for example early access. To group payers by how much they spend instead, use the RFM spend tiers below. Feeds the [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) journey | | First-time payers | * `PW_InAppPurchase` exactly 1 time, on or after the date you started tracking purchases | Tell a player’s first purchase from later ones. Feeds the [Second purchase offer](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/purchases/#second-purchase-offer) journey | | Recent payers | - `PW_InAppPurchase` exactly or more, 1 time, during the last 90 days | Tell payers apart from other players, for example to send them a different offer in the [Reactivation for inactive players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) journey | | Achievement hunters | * `Achievement unlocked` exactly or more, 5 times, during the last 30 days | Offer exclusive content to players who collect many achievements. Feeds the [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) journey | | Stuck players | - `Level failed` exactly or more, 3 times, during the last 3 days - AND `Level completed` never, during the last 3 days | Offer help to players who keep failing a level and have not cleared one lately, before they quit. Feeds the [Help for stuck players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/new-and-active-players/#help-for-stuck-players) journey | | Daily engaged players | * `PW_ApplicationOpen` exactly or more, 5 times, during the last 7 days | Announce live events or early access to players who open the game often. `PW_ApplicationOpen` counts each time the game comes to the foreground, not only cold launches | | Lapsed payers | - Segment [Recent payers](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#recent-payers) - AND segment [Lapsed players](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#lapsed-players) | Give players who paid recently and then stopped playing their own comeback offer, for example in a copy of the [Reactivation](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#reactivation-for-inactive-players) journey. Built from [existing segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-existing-segments/) | ## Segments by tags These segments are built from player tags. Use them as a starting point or for inspiration: change the values, or build your own segments. To set the filters, see [Build segments by tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/). Most of them need the tags from [Store player data in tags](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#store-player-data-in-tags). | Segment | Filter | Use it to | | -------------------- | -------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | | Players without push | `Push Alerts Enabled` is false | Find players who declined or never allowed notifications, for example to ask them again with an in-app message or to reach them by email | | Non-paying players | `is_paying_user` is false | Send messages meant for players who have not paid yet | | Low-balance players | `soft_currency_balance` less or equals `200` | Offer a currency pack. Choose the number that counts as low in your game | | VIP players | `vip_tier` is `gold` | Give VIP players their own offers and early access | ### Install anniversary This segment finds players who installed the game on this day in an earlier year. It feeds the [Anniversary reward](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#anniversary-reward) journey, which checks it every day. To build it: 1. Add a filter by the `First Install` tag. 2. Open the time frame dropdown and choose **is today** in the **ANNIVERSARY** section. 3. Add a second `First Install` filter with **N or more days ago** set to 365, joined with **AND**. The anniversary operator compares only the month and day, so without this filter the segment also includes players who installed today. --- # Collect player data **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/ Identify players by User ID, send events from the game, and store player data in tags for your mobile game in Pushwoosh. --- Pushwoosh needs to know who your players are and what they do in the game. Events record the actions, and tags keep the current state of each player. Let AI Assistant create the events and tags [AI Assistant](https://docs.pushwoosh.com/product/pushwoosh-ai/ai-assistant/) can create the events and tags from this page in your application. Send it the link to the page and ask it to make the tags User-specific. Check each change before you approve it. ## Identify players by User ID Developer assistance needed Setting the User ID is a change in your game’s code. Please share [Collecting User IDs](https://docs.pushwoosh.com/developer/first-steps/collecting-user-ids/#setuserid-sdk-method) with your developers. Pushwoosh links all of a player’s devices to one User ID, so your segments and journeys treat one person as one player. Events and User-specific tags that a device recorded before the player logged in stay with its earlier ID. Device-specific tags move with the device. By default, Pushwoosh uses each device’s hardware ID (HWID) as its User ID. A player who plays on two devices then counts as two players. To avoid this for players who log in, set your own account ID as their User ID: 1. Choose the ID to use. Take the account ID that your game already assigns to each player. 2. Ask your developers to call `setUserId` with this ID when the player logs in. 3. Check the result. Log in to the game on a test device, then open **Audience** → **User Explorer**, search by **User Id**, and enter the account ID. The player’s profile opens. Players who never log in keep the HWID as their User ID, and Pushwoosh sets it for them. To learn more about how User IDs relate to devices, see [Users and User IDs](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/). ## Set up the events to track Events are how Pushwoosh learns what a player does. Create them first: [segments](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/) group players by the events they triggered, and [journeys](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/) start when an event happens. Plan both the events and their attributes before you build anything. [Recommended events for gaming apps](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/) lists ready-made gaming events with attributes, and [Custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) explains how to define your own. There are two kinds of events: * [Default events](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#default-events): device registration, app open, purchase, and a few more. The SDK sends them, and you do not create them in the Control Panel. * [Custom events](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#custom-events): level completed, reward claimed, store opened, and others. Your developers [send them from the game](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#send-the-events-from-the-game), and Pushwoosh creates each one the first time it arrives. To choose the attribute types yourself, create the event in the Control Panel first. See [Custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) and [Events created automatically](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#events-created-automatically). ### Default events The SDK sends these events for you. You need them for most of the journeys in this playbook: onboarding, win-back, and purchase offers. * [`PW_DeviceRegistered`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered): a device received its first push token. The SDK requests the token only when the game calls `registerForPushNotifications`, which on iOS also shows the permission prompt, and the token arrives whatever the player answers. If the game asks for permission later than the first launch, the event fires at that moment, not at install. To catch new players, use the `Tutorial started` event instead. * [`PW_ApplicationOpen`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_applicationopen): a player opened the game. You need it to find players who still play and players who stopped playing, so you can win them back. * [`PW_InAppPurchase`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_inapppurchase): a player bought something in the game. You need it to tell payers from non-payers, for example to offer a first purchase only to players who have never paid. Your developers turn on purchase tracking in the iOS SDK or send purchases from code, and on Android they always send them from code. On iOS, it fires only for products configured in App Store Connect. * Optional: [`PW_UserIdle`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_useridle): fires when a player stops touching the screen for a time you set, at least 30 seconds. Starts a journey that shows an in-app hint when a player stalls. Your developers enable it in the SDK configuration. * Optional: [`PW_ApplicationExit`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_applicationexit): fires when a player leaves the game and does not return within 10 to 30 seconds. Starts a win-back journey for players who leave mid-flow. Your developers enable it in the SDK configuration. An event appears in **Audience** → **Events** after the SDK sends it for the first time. To add any other default event to the list earlier, for example to build a segment before the game update is live: 1. Open **Audience** → **Events** and click **Create event**. 2. Choose **Default Event**. 3. Select the event and click **Activate**. The list in step 3 does not include `PW_UserIdle` and `PW_ApplicationExit`. They appear in **Audience** → **Events** only after the SDK sends them for the first time. For details on these and other default events, see [Default events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/). ### Custom events Custom events record what happens in your game, such as completing a level. You choose which ones the game needs, your developers send them, and you check that they arrive. #### Choose the events to track Choose the events your game needs and the attributes each one carries, for example `level_id` for `Level completed`. The table gives examples: use them as they are, rename them, or add your own. Track only the events that the journeys you launch need. | Event | When it fires | Key attributes | What it drives | | ------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- | ------------------------------------------------ | ----------------------------------------------------------------------------- | | [New level](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#new-level) | Player reaches a new level | `level_id` (String) | Optional: your own milestone segments | | [Level completed](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#level-completed) | Player finishes a level | `level_id` (String) | Engaged non-payers and stuck players segments, Help for stuck players journey | | Level failed | Player fails a level | `level_id` (String) | Stuck players segment and journey | | Tutorial started | Player starts the in-game tutorial | `tutorial_name` (String) | Onboarding journey, tutorial drop-off segment | | [Tutorial completed](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#tutorial-completed) | Player finishes the in-game tutorial | `tutorial_name` (String), `completion` (Boolean) | Onboarding journey, push permission flow | | [Virtual currency earned](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#virtual-currency-earned) | Player’s in-game currency balance tops up | `currency_name` (String), `quantity` (Integer) | Optional: your own currency-based segments | | [Achievement unlocked](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/recommended-events-for-gaming-apps/#achievement-unlocked) | Player unlocks an achievement | `achievement_name` (String), `level` (Integer) | Achievement hunters segment | | Daily reward claimed | Player claims the daily reward | `day_number` (Integer) | Daily reward reminder journey | | Live event joined | Player joins a live event | `event_id` (String) | Conversion goal of the live event journey | | Store opened | Player opens the in-game store | `section` (String) | Store abandonment journey | No tutorial in your game? Use your own events for the first two actions every new player takes, such as starting and finishing the first level, instead of `Tutorial started` and `Tutorial completed` wherever this playbook mentions them. * The first event starts the onboarding journey, and the second one tells it the player is done. Skip the `completion` condition that goes with `Tutorial completed`. * The SDKs send no first-launch event of their own, so both have to be custom events your game sends. #### Send the events from the game Developer assistance needed Sending events is a change in your game’s code. Please share [Events for developers](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/) and the [gaming event examples](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events/custom-events-examples#gaming-apps) with your developers. They send each event with `postEvent` at the moment the action happens. Purchases and reward grants should come from the game server with the player’s `userId`. Agree with your developers which events the game sends and which attributes each one carries. The names of the events and attributes must match exactly. For example, this is how the game sends `Level completed` with its `level_id` attribute: * Android ```java TagsBundle attributes = new TagsBundle.Builder() .putString("level_id", "12") .build(); InAppManager.getInstance().postEvent("Level completed", attributes); ``` * iOS ```swift PWInAppManager.shared().postEvent("Level completed", withAttributes: ["level_id": "12"]) ``` If an event happens on a server that already sends its own webhooks, such as your game backend or a tournament service, it can reach Pushwoosh through an [inbound webhook](https://docs.pushwoosh.com/product/integrations/inbound-webhook/) instead of `postEvent`. You map the fields it sends to a Pushwoosh event in the Control Panel, and the event can start a journey like any other. To check that an event arrived, open **Audience** → **User Explorer**, find a test player, and open the **Events history** tab. ### Track revenue as PW_Conversion `PW_Conversion` records the amount and currency of every purchase in one format. You need it for a revenue goal in journeys and for spend tiers in [RFM segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/), which groups players by how recently, how often, and how much they buy. Purchases that the iOS SDK tracks reach `PW_Conversion` on their own, because they carry the `__amount` and `__currency` attributes. Purchases from Android need one of these: * If your game runs on both iOS and Android, ask your developers to send Android purchases as `PW_InAppPurchase` with numeric `__amount` and `__currency` attributes through `postEvent`, instead of the SDK’s purchase method. Then the same automatic mapping covers both platforms. * If your game runs on Android only, map the purchases yourself, without new code. An event keeps one mapping, so in a game with iOS purchases a manual mapping would replace the automatic one. To map purchases yourself: 1. Open **Audience** → **Events** and open the conversion events settings. 2. Add `PW_InAppPurchase` as a conversion event. 3. Select the attributes that hold its **Price** and **Currency**. 4. Click **Apply**. Caution Use only one path per transaction. If your code also sends `PW_Conversion` for the same purchase, Pushwoosh records two separate conversion events. For all the setup options, see [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/). ### Track subscriptions from the stores If your game sells subscriptions, such as a VIP pass or a battle pass, Pushwoosh receives their status changes from the App Store and Google Play. Each change arrives as a `PW_Subscription*` event, for example: * `PW_SubscriptionStart`: a player subscribes. * `PW_SubscriptionCancel`: a player turns off auto-renewal. * `PW_SubscriptionBillingIssue`: a renewal payment fails. Use these events to start journeys, for example a reminder before a cancelled pass expires. Developer assistance needed The game must pass the player’s Pushwoosh HWID with each purchase: * On iOS, as `appAccountToken`. * On Android, as `obfuscatedAccountId`. The game passes the value of the SDK’s `getSubscriptionAccountId()`, which is the Application Code and the HWID together, not the HWID alone. Without it, Pushwoosh receives the notification but records no event, and subscriptions bought earlier can’t be matched later. Please share [App Store subscription tracking](https://docs.pushwoosh.com/product/integrations/app-store-subscription-tracking/) and [Google Play subscription tracking](https://docs.pushwoosh.com/product/integrations/google-play-subscription-tracking/) with your developers. The events cover subscriptions only. One-time purchases, such as a coin pack, still arrive as `PW_InAppPurchase`. ## Store player data in tags A [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/) is a value that Pushwoosh stores on a player’s profile, such as the player’s level or whether the player has paid. An event records what happened, and a tag keeps the current state. Tags help you to: * Send a message only to players with a certain value, for example an offer only to paying players. * Put the value into the message text, for example the player’s level. See [Build the journeys](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/). Tags are optional. The journeys in this playbook run on events and on default tags that Pushwoosh sets itself, so you can skip your own tags at first. Add them when you want more ways to group players. See [Segments by tags](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#segments-by-tags). ### Your own tags Your developers send tag values from the game, or you can [import them from a CSV file](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/update-tags-via-csv/). Pushwoosh creates a tag the first time your developers send it, as a User-specific tag. Create the tag in the Control Panel first if you want to choose its type yourself. See [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). These tags are examples. Rename them to match your game’s vocabulary, and add your own: | Tag | Type | Example | What it drives | | ----------------------- | ------- | ------- | ----------------------------------------------------- | | `player_level` | Integer | `12` | Filter messages to a progression tier | | `is_paying_user` | Boolean | `true` | Split payer and non-payer messaging | | `soft_currency_balance` | Integer | `1500` | Offer a currency pack to players whose balance is low | | `vip_tier` | String | `gold` | Give VIP players their own offers and early access | | `favorite_mode` | String | `pvp` | Announce live events for the mode the player likes | When you create a tag in the Control Panel, keep [**User-specific**](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#tag-scope-general-vs-user-specific) turned on, as it is by default. Pushwoosh then keeps one value per User ID, so the value belongs to the player and not to one of their devices. With **User-specific** turned off, each device keeps its own value. See [Identify players by User ID](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id). ### Default tags Pushwoosh stores these [default tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) for every device, so you do not create them: * **Language**: the language of the device. Pushwoosh uses it to send each player the message in their own language. * **Last Application Open**: when the game was last opened on the device. Use it to find players who stopped playing. * **Push Alerts Enabled**: whether notifications are allowed in the device settings. Use it to show an in-app message only to players who have not allowed them yet. * **First Install**: when the game was first installed on the device. Use it for install anniversaries. * **Country**: the country of the device. Use it to send an offer or a live event announcement only to players in one region. --- # Launch and measure **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/ Test each mobile game journey in Pushwoosh before launch, read its statistics, and measure whether it changes player behavior. --- Test every journey on your own phone before real players get its messages, then check whether it works. ## Test each journey before you launch it A journey has no test mode, so once it is running, real players who match its entry get its messages. Testing on your own phone first catches a missing event, an empty segment, or a wrong message before players see it. Run through this list for each journey: 1. Install the version of your game that includes the Pushwoosh SDK on your phone, and log in with a test account. If you do not have one, ask your developers for a test version. The phone must register in the application where the journey runs. To send test messages from presets, also register it as a [test device](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/test-devices/). 2. Play the game and do what the journey needs, for example complete a level. Then open **Audience** → **User Explorer**, find the test device, and check **Events history**. The events must be there with the expected attributes, or the segment and the journey do not see them. 3. If the journey starts from a segment, open it in **Audience** → **Segments** and refresh its size to see whether it counts any players. 4. Launch the journey for your test players only. Build a test segment from your test User IDs with the **User ID** filter. If the journey starts from a segment, use the test segment as its entry. See [How to test a journey with a test segment](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/test-your-integration/#how-to-test-a-journey-with-a-test-segment). Important: journeys that start on an event If the journey starts on an event (a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/)), every player who triggers the event enters it, including real players, and the entry has no segment filter. To keep real players out of the messages, use one of these: * Right after the entry, add a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) on your test segment and leave its **All other users** branch empty. Real players enter but leave the journey at once without messages, and they count in its entries. Remove the split before the real launch. * [Clone the journey to a test application](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#clone) and run it there with a phone that is registered in that application. Events the test application lacks are created there, but check the segments and the content of the clone. 5. Start the journey on the test phone: do the action that the entry waits for. Check that the message arrives, and follow the test player with [Find user path](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/). 6. After a day, check how many players entered the journey. See [Read the journey statistics](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/#read-the-journey-statistics). ## Read the journey statistics Journey statistics show whether players enter a journey, get its messages, and reach its goal. To see them: 1. Open the journey on the canvas. The statistics panel on the left shows the whole journey. 2. For numbers on each element, open Canvas settings (the gear icon in the bottom-right corner) and turn on **Show metrics**. See [Journey element statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/journey-element-statistics/). For what each metric means, see [Overall journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/overall-journey-statistics/). In the first week after launch, look for these signals: | If you see | It usually means | What to do | | --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | No entrances | The entry segment is empty or the entry event does not arrive | Repeat steps 2 and 3 of [Test each journey before you launch it](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/launch-and-measure/#test-each-journey-before-you-launch-it) | | Far fewer messages sent than entrances | Many players can’t get push, or frequency capping and the silence period hold messages back | Check **Frequency capping** in **Communications**, add an in-app or email branch after a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/), and check your [delivery rules](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-delivery-rules-before-you-message-players) | | Many drop-offs | Players left the journey because of a problem, such as a device that was not found or an invalid push preset | Expand the **Drop-offs** block on the element to see the reasons | | Messages are opened, but few players reach the goal | The message works, but the offer or the moment does not | Test another text or offer with an [A/B/n split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) | Journey does not behave as expected? If a journey does not behave as expected, follow one player through it with [Find user path](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/analyze-user-path/), and look up why players leave a journey or skip a message in [Drop-off and skip reasons](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/). Journey statistics count goal conversions, not revenue. To compare revenue, use [Conversion events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/conversion-events/). ## Measure results To measure whether a journey changes player behavior, use the three tools below, from simplest to strongest. * **Conversion goals:** every journey in this playbook has one. The goal can be reached within the period you set, up to 30 days after the player leaves the journey. See [Conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals). * **Holdout Split:** hold back 1% to 20% of the players who reach a [Holdout Split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/holdout-split/) element and compare their goal rate with everyone else. The **Goal uplift vs holdout** report updates once a day. Where to place a Holdout Split * Put it right after the entry, before any message. Connect only the **In journey** branch and leave **Holdout** empty: held-out players leave the journey there and get nothing. * The journey needs a goal, because the report compares goal rates between the two groups. * To measure one part of a journey, put another Holdout Split in front of that part. * **Control group:** by default, holds out a share of the players from all marketing messages in the application, so you can measure the combined effect of your messaging. Set it up before you launch. See [Set up a control group before you launch](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#set-up-a-control-group-before-you-launch). To read the results, see [Analyze control group impact](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/analyze-control-group-impact/). ## Common pitfalls These are the mistakes that most often leave a segment empty or a journey silent in a mobile game. Check them before you launch a journey. | Pitfall | What happens | What to do | | ------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | A guest who plays on two devices | Until the player logs in, each device has its own User ID, so one player counts as two | [Identify players by User ID](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id) | | iOS push permission prompt | iOS shows it only once, so a player who declines on the first launch can’t be asked again | [Ask for push permission at the right moment](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/#ask-for-push-permission-at-the-right-moment) | | Repeating events such as `Level completed` | They fire many times a day and start the journey again and again | Set a campaign entry limit. See [Best practices for every journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/#best-practices-for-every-journey) | | `PW_ApplicationOpen` | It fires each time the game comes to the foreground, not only on cold launches, so open counts run higher than sessions | [Segments by events](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-segments/#segments-by-events) | | Purchases | On iOS, only products configured in App Store Connect are tracked, and on Android the game sends purchases from code | [Default events](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#default-events) | | Store subscriptions | Without `appAccountToken` on iOS or `getSubscriptionAccountId()` on Android, subscription events are not recorded | [Track subscriptions from the stores](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#track-subscriptions-from-the-stores) | | The Unity plugin | It has no Message Inbox, so a Unity game can’t show App Inbox messages | Use push or in-app instead. See [Live event and VIP announcements](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/loyalty-and-win-back/#live-event-and-vip-announcements) | --- # Prepare for communication **URL:** https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/prepare-for-communication/ Ask mobile game players for push permission at the right moment, set delivery rules, and set up a control group before launch. --- These settings decide how players experience your messages and whether you can tell what works. Set them up before your first message, so players are not asked or messaged at the wrong time and you can measure the results from the first day. ## Ask for push permission at the right moment Developer assistance needed Moving the push permission request out of the first launch is a change in your game’s code. Please share [Before you start](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#before-you-start) with your developers. On iOS, the system prompt appears only once per install, and if the player taps **Don’t Allow**, the game can’t show it again. So do not ask on the first launch, when the player does not know the game yet. Ask in two steps instead. First, show a custom in-app message that explains what notifications give the player. Only its **Allow** button opens the system prompt. Show the message when the player already enjoys the game. Choose the event that marks that moment. The message appears the next time the player opens the game after the event. For example: * After the tutorial: the `Tutorial completed` event. * After the first level: the `Level completed` event. * After the first reward: the `Daily reward claimed` event. * After the first achievement: the `Achievement unlocked` event. To set it up: 1. Build the in-app message. See [Build the in-app](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#build-the-in-app). 2. Create a journey that starts on the event you chose. See [Trigger the in-app before permission is requested](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#trigger-the-in-app-before-permission-is-requested). For `Tutorial completed`, add the condition `completion` is true. 3. If the event repeats, such as `Level completed`, open **Settings**, select **Campaign Entry Limit**, choose **Custom period**, and set **Once in** to **a lifetime**, so the player sees the message once. 4. Skip players who already allowed notifications. See [Skip the prompt for users who already opted in](https://docs.pushwoosh.com/product/content/in-apps/show-in-app-before-push-permission-prompt/#skip-the-prompt-for-users-who-already-opted-in). For players who decline, the [journeys](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/) reach them through other channels. A [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) sends players down different branches depending on the channels where you can reach them. ## Set delivery rules before you message players [Delivery rules](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/) decide how often a player hears from you, so set them before the first journey goes live. Games run several journeys at once, and without limits one player can receive an onboarding nudge, a reward reminder, and an offer on the same evening. 1. Go to **Settings** → **Message delivery settings** → [**Frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). 2. Turn on the channels you use, such as push, in-app, and email. 3. Set the maximum number of messages and the period for each channel. Allowed values are 1 to 1,000 messages per period, and the period is 1 to 30 days. 4. Choose a counting mode for each channel: **Rolling window** or **Calendar day**. 5. Click **Apply changes**, then **Confirm**. Journey messages use the global limits by default. To change the limits of a journey that is running, pause the journey first. For the full behavior, see [Global frequency capping](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/). Each journey also has its own controls: silence period, campaign entry limit, and message type. See [Best practices for every journey](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/build-the-journeys/#best-practices-for-every-journey). ## Set up a control group before you launch A [control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/) holds a share of players out of all marketing messages in the application, or in the countries or tag you limit it to, so you can compare them with everyone else and see what your messaging changes. Create it before you launch the journeys, so the comparison covers them from the first day. Control groups work by User ID The group selects players by User ID. Every device the SDK registers has one, the device’s hardware ID (HWID) until the player logs in, so a guest who plays on two devices can be held out on one and messaged on the other. See [Identify players by User ID](https://docs.pushwoosh.com/tutorials/industry-playbooks/mobile-games/collect-player-data/#identify-players-by-user-id). To create a control group, you need permission to modify the application. 1. Go to **Settings** → **Control groups** and click **Add control group**. 2. Enter a name for the group. 3. Set **Group size**, from 1% to 20%. 5% is recommended for most apps. 4. Choose a mode. **Permanent** keeps the same players out until you turn the group off. **Auto-refresh** picks new players every 1, 2, or 3 months and returns the previous ones to messaging. **Experiment with an end date** returns everyone to messaging on the date you set. 5. Click **Create**. As soon as the control group is on, the players in it stop receiving marketing messages in any channel, so your journeys reach fewer players by that share. Transactional messages still reach them. For all the settings, see [Create and manage a control group](https://docs.pushwoosh.com/product/audience-data-and-segmentation/control-groups/create-and-manage-control-group/). --- # Email **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/ Set up double opt-in, abandoned cart, catalog, recommendations, and sunset flow email campaigns in Pushwoosh, step by step. --- These email journeys cover common list and commerce flows you build in Customer Journey Builder. Open a guide when email is the channel you want to set up end to end. [Create a double opt-in campaign ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-create-a-double-opt-in-campaign)Confirm consent after signup so the list stays clean and deliverable. [Set up an abandoned cart campaign ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign)Follow up when a shopper leaves items in the cart. [Send catalog products in email ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-send-catalog-products-in-email)Pull items from your Product Catalog into a promotional email. [Set up product recommendations in email ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email)Suggest products from a recommendation strategy based on behavior. [Set up an email sunset flow ](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow)Stop emailing inactive subscribers to protect engagement and sender reputation. --- # How to create a double opt-in email campaign **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-create-a-double-opt-in-campaign/ Learn how to create a double opt-in email campaign in Pushwoosh to improve deliverability and ensure user consent. --- To keep your email audience high-quality and [comply with legal and deliverability standards](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/), don’t add users to your mailing list right after they enter their email, such as during registration or via a form. Instead, use a double opt-in process to confirm their subscription. #### What is double opt-in? **Double opt-in** is a two-step subscription method where users must confirm their intent to receive emails. After submitting their email address, they receive a confirmation email with a verification link. Only once they click this link are they added to your mailing list. This extra step verifies consent and helps ensure your list consists of genuinely interested recipients. Using double opt-in is important because it: * Confirms user intent, ensuring you email only those who want to hear from you * Improves deliverability by increasing engagement and reducing bounce rates * Minimizes spam complaints and protects your sender reputation * Helps meet legal and compliance obligations regarding user consent ### Send a double opt-in email using Customer Journey To automate the double opt-in process after a user registers using [Customer Journey](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), follow these steps: #### Create a triggered email campaign Go to **Customer Journey Builder → Create campaign → Email** and select **Triggered Campaign**. ![Customer Journey Builder interface showing Create campaign button with Email channel and Triggered Campaign option](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-1.webp) #### Configure the Entry element In the [Trigger-based entry element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/), select the default [PW_DeviceRegistered event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered) under **Event**. This event is triggered whenever a new device (including email addresses) is registered in your Pushwoosh project. Below the event, define a condition to ensure the Journey starts only for email registrations: * **Field**: PW_platform_id * **Operator**: is * **Value**: Email ![Trigger-based entry element configured with PW_DeviceRegistered event and condition for email platform registration](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-2.webp) This condition filters the trigger so that only devices registered via email will start the Journey. Click **Save** to apply your changes. #### Add and configure the confirmation email content Drag an [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) onto the canvas and configure it. In the Content section, select [a preset](https://docs.pushwoosh.com/product/content/email-content/) designed for double opt-in. The preset should include a confirmation link and a message asking users to verify their subscription. Example email content ***Subject:** Please Confirm Your Subscription* ***Body:***\ *Thank you for signing up!*\ *To start receiving our emails, please confirm your subscription by clicking the link below:* *👉 [Yes, I Confirm My Subscription]* *If you didn’t sign up, no action is needed — you won’t receive any emails from us.* *We’re excited to have you on board!*\ *— The [Your Company] Team* Once you’ve set the subject line, added the content, and finished configuring the email element, click **Save**. ![Email element on canvas with Content section showing preset for double opt-in confirmation email template](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-3.webp) #### Monitor user confirmation Add a [Wait for Trigger element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) to monitor whether the user clicks the confirmation link. Configure the element: 1. First, set the wait duration (e.g., 4 days). ![Set the Wait for Trigger element](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-4.webp) 2. Next, create a branch for users who confirmed the subscription. Name the branch, for example, **Confirmed** and select the [default event PW_EmailLinkClicked.](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_emaillinkclicked) This event will track whether the user clicks the confirmation link in the email.\ A fallback branch (Not Triggered) is created automatically for users who do not confirm. ![Set the Wait for Trigger element](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-5.webp) 3. Click **Save** to apply the configuration and continue building your journey. After sending the confirmation email, the Journey waits up to 4 days for the user to confirm. #### Tag users who didn’t confirm as unsubscribed If a user doesn’t click the confirmation link in your double opt-in email, you can mark them as unsubscribed to avoid sending them future emails. For this: 1. Add the [Update User Profile](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/update-user-profile/) element to your Journey. 2. Connect it to the **Not Triggered** branch of the **Wait for Trigger** element. 3. In the Manual Tag Value section choose the **`Unsubscribed Emails`** tag and set the value to **`true`**. ![Tag users as unsubscribed ](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-6.webp) This way, only users who confirm their email will stay subscribed and continue receiving your emails. #### Finalize the Journey * End the **Confirmed branch** — connect it to the Exit element or leave it without an outgoing connection. * End the **Not Triggered** branch the same way. ![ Finalize the Journey](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-double-opt-in-campaigns-7.webp) #### Activate the Journey Click **Launch campaign** to launch the journey. --- # How to send an email with products from your catalog **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-send-catalog-products-in-email/ Create promotional emails with products from your Pushwoosh Product Catalog and send them once or through Customer Journeys. --- Use this guide to send an email campaign that promotes products from your Pushwoosh [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/). Example: a “Weekly New Arrivals” email for an electronics category. Send a one-time version with a few hand-picked products, or a recurring version that a Customer Journey sends every Monday with the newest electronics in the catalog that week. Tip Want to show each recipient their own abandoned cart instead of a shared product pick? See [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/). Want products ranked by bestsellers, past behavior, or purchase patterns instead of a fixed rule? See [How to set up product recommendations in email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/). ## Before you begin Make sure your account’s catalog has products in it. Go to **Content → Product Catalog** and connect a feed, import a CSV, or add products by hand. [Learn how to populate your catalog](https://docs.pushwoosh.com/product/content/product-catalog/#ways-to-populate-your-catalog). ![Product catalog list with search, category filter, product rows, and an open row menu with Edit and Delete actions](https://docs.pushwoosh.com/content-product-catalog-5.webp) Catalog rule requirements Pulling products automatically by category (**Catalog rule**) needs an [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/) (**Settings → API Access**). If you only need a fixed, hand-picked set of products, none of that is required. See [Add products manually](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#add-products-manually). ## Build the email 1. Go to **Content → Email Content** and create an email in the [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/). Name it something like “Weekly New Arrivals Electronics”. 2. Drag a **Products** block onto the canvas. ![Selected Products block on the email canvas showing a two-column grid of placeholder product cards](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-2.webp) 3. In the block’s settings panel, under **Source**, select [**Manual**](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-send-catalog-products-in-email/#pick-specific-products) to hand-pick a fixed set of products, or [**Catalog rule**](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-send-catalog-products-in-email/#pull-products-with-a-catalog-rule) to have Pushwoosh select products from the catalog at send time. How product sources stay up to date **Manual** stores the products in the block when you select them. Later catalog changes to the price, image, or stock do not apply until you click **Refresh from catalog**. **Catalog rule** does not store a fixed list. At each send, it pulls matching products from the catalog again. ### Pick specific products Use **Manual** when you want full control over exactly which products appear. For example, a one-time email highlighting four specific new electronics items. 1. Select **Manual** under **Source**. 2. Set **Number of products** to the number of cards to show (1-24). For this example, use 4. 3. Click **Pick from catalog…**, filter to the **electronics** category, and check the products to feature. ![Pick from catalog dialog listing electronics products with checkboxes, a search field, and a category filter, with two headphones and laptop products checked](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-3.webp) 4. To replace a product’s picture, click its thumbnail under **Product images**. 5. Products picked from the catalog carry over their real product page URL under **Product links** automatically. If you add a product manually instead of picking it from the catalog, its link starts blank. Fill it in yourself. ![Product images thumbnails and Product links fields for four electronics products: headphones, a laptop, a camera, and speakers](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-7.webp) 6. Style the grid and card template as needed. See [Style the grid](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#style-the-grid). ### Pull products with a catalog rule Use **Catalog rule** when the picks should stay current automatically. For example, a recurring “Weekly New Arrivals” send that should always show whatever is newest in electronics, without you editing the email each week. 1. Select **Catalog rule** under **Source**. 2. Set **Category** to **electronics** (or **All categories**). 3. Choose a **Sort** order: **Newest first** for a new-arrivals email, or **Price: low to high**, **Price: high to low**, **Title A-Z**. 4. Set **Number of products** (1-12). For this example, use 4. ![Products block set to Catalog rule with Category set to electronics, Sort set to Newest first, and a live preview of four matching products](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-4.webp) 5. Click **Refresh preview** to recalculate the canvas preview against the catalog. 6. Style the grid and card template as needed. See [Style the grid](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#style-the-grid). The canvas shows today’s catalog matches. Recipients get live products at send time. Note If the category has fewer products than **Number of products**, the grid shrinks to however many actually match. There are no empty placeholder cards. See the [Products block reference](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) for details about sorting, product images, links, and sale prices. ### Finish building the email Finish the rest of the email, including the subject line, other content blocks, and footer. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/). ## Send the email After you save the email content, choose how to send it. A **Manual** email works well for a one-time send. A **Catalog rule** email works well in a recurring Journey that keeps sending an up-to-date “Weekly New Arrivals” email on a schedule. This guide builds one of each below. If you only need one approach, build just that version instead of both. ### One-time email Use a one-time email for a single promotion, such as the hand-picked electronics highlight built above. Select the email content you created, choose a segment or your full audience, then send immediately or schedule it for later. See [Send one-time emails](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails/). ![One-time email Content step with the Weekly New Arrivals Electronics email selected and a preview showing four real catalog product cards](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-8.webp) ### Customer Journey For a recurring or triggered send, add the **Catalog rule** email to a Customer Journey. This keeps the product selection current at send time without touching the email content again. Tip Use it for a weekly new arrivals or back-in-stock email without updating the email content. 1. Go to **Campaigns → Customer Journey Builder → Create campaign → Email**. ![Email campaign type picker with One-time campaign, Recurring campaign, and Triggered campaign options](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-5.webp) 2. Select **Recurring campaign** for a schedule, such as the weekly send, or **Triggered campaign** for an event from your backend, such as a back-in-stock event. 3. Configure the [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) element: * **Audience Source:** select a segment, such as **All emails**. * **Entry schedule:** select **Recurring entry → Periodically**. * **ENTRY EVERY:** set to **1 Week**. * **ON SELECTED DAYS:** check **Monday**. * **Time:** set to **09:00**. * **Timezone:** leave as **Subscriber’s device timezone**. For subscribers without device timezone data, Pushwoosh falls back to the **Fallback timezone** set next to it. By default that is the timezone your browser was in when you configured the entry. Double-check it matches what you want. ![Audience-based Entry element configured with a recurring weekly schedule: every 1 week, Monday, at 9](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-6.webp) 4. Open the **Email** step and select the **Weekly New Arrivals Electronics** email you built above as its content. 5. Finish configuring the remaining Journey settings and elements as needed. See the [Customer Journey overview](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). 6. Click **Launch campaign** to activate the Journey. ![Recurring Customer Journey canvas with an Audience-based Entry set to every 1 week on Monday, connected to the Weekly New Arrivals Electronics email, then Exit](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-send-catalog-products-in-email-9.webp) Every Monday, the entry element re-enrolls the audience and the **Catalog rule** block pulls whatever electronics products currently match. You do not need to reopen the content each week. With **Manual**, refresh the block when the catalog changes. Track opens, clicks, and conversions on the [Journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics/) page, or in [Email statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/messaging-statistics/email-statistics/) for the one-time send. --- # How to set up an email sunset flow **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow/ Stop marketing emails to inactive contacts with the sunset policy and ask them to reconfirm in a journey before they're cut off. --- A sunset flow stops emailing contacts who no longer read your emails. This protects your sender reputation and deliverability. You can set it up in two ways: * **[Sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts) only.** Pushwoosh stops marketing emails to contacts who haven’t opened or clicked your emails for a period you choose. You don’t need a journey. * **Sunset policy and a reconfirmation journey.** A journey asks inactive contacts whether they still want your emails and unsubscribes those who don’t answer. This gives contacts a chance to stay before the policy stops emailing them. This guide shows both: first [turn on the sunset policy](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow/#turn-on-the-sunset-policy), then [create the reconfirmation journey](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set--up-email-sunset-flow/#create-the-reconfirmation-journey). ## Turn on the sunset policy The [sunset policy](https://docs.pushwoosh.com/product/messaging-channels/emails/email-deliverability/#stop-marketing-emails-to-inactive-contacts) is optional and off by default. You turn it on for each project in the [email platform settings](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification/#turn-on-the-sunset-policy). Transactional emails ignore it. 1. Go to **Settings → Configure Platforms** and click **Platform settings** in the **Emails** row. 2. On the **Sunset policy** card, click **Enable**. ![Sunset policy card in the email platform settings with the Enable button highlighted](https://docs.pushwoosh.com/emails-email-deliverability-sunset-1.webp) 3. In **What counts as activity?**, choose **Click or open**. 4. In **How long before a contact becomes inactive?**, choose the period, for example, 180 days. ![Enable sunset policy window with Click or open selected and the period set to 180 days](https://docs.pushwoosh.com/emails-email-deliverability-sunset-2.webp) 5. Click **Enable**. From now on, marketing emails skip contacts whose last open or click is older than the period you chose. Skipped contacts aren’t unsubscribed. They receive marketing emails again after they open or click an email that was sent with **Ignore sunset policy** turned on. ## Create the reconfirmation journey 1. Go to **Campaigns → Customer Journey Builder**, then click **Create Campaign → Email**. 2. Select **Build a Journey from Scratch**. ### Choose the contacts to reconfirm Use an [Audience-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) with a segment of email contacts who haven’t opened or clicked your emails for a while. Pick a period a little shorter than the policy period, for example, 150 days for a 180-day policy, so contacts get the question before the policy stops their emails. Note Before you build the segment, check that **PW_EmailOpen** and **PW_EmailLinkClicked** are in the event list. If either is missing, go to **Audience → Events**, click **Create event**, choose **Default Event**, and click **Activate** next to the event. Each event appears in the list after the first open or the first click in your emails. 1. In the Audience-based Entry element, click **Create Segment → Build segment**. 2. Name the segment, for example, **No opens or clicks for 150 days**. 3. Add a **User ID** filter, switch it to **Email**, choose **contains**, and enter **@**. This keeps only email contacts in the segment. 4. Click **Filter** and add an **Event** filter: choose **PW_EmailOpen**, then **never**, **during the last N days**, and enter **150**. 5. Add a second **Event** filter with the same settings for **PW_EmailLinkClicked**. 6. Add a **Tag** filter: choose **First Install**, then **N or more days ago**, and enter **150**. This leaves out contacts added less than 150 days ago, who haven’t had time to open your emails yet. ![Segment of email contacts with no opens or clicks during the last 150 days](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-2.webp) Note All filters in the group must match, so the segment includes only email contacts older than 150 days who neither opened nor clicked during that time. Without the **User ID** filter, a mobile or web device of the same user can enter the journey instead of the email contact. The unsubscribe step then applies to that device, not to the email contact. 7. Click **Save**. 8. Make sure the new segment is selected in **Audience source**. ![Audience-based Entry with the No opens or clicks for 150 days segment](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-1.webp) 9. Click **Save**. ### Add the reconfirmation email 1. Drag an [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) onto the canvas after the entry. 2. Select a preset that asks contacts whether they still want your emails, or create a new one. Include a confirmation link, because a click on it is what keeps the contact subscribed. ![Email element with the Email preset field selected and the Create content button](https://docs.pushwoosh.com/journey-elements-README-14.webp) Example email content ***Subject:** Do You Still Want to Hear from Us?* ***Body:*** *We noticed you haven’t engaged with our emails in a while. If you’d still like to receive updates, news, and special offers, please confirm by clicking the link below:* *👉 [Yes, keep me subscribed]* *If you don’t respond, we’ll stop sending you emails to avoid cluttering your inbox.* *Thank you for being with us!* 3. Under the preset, choose the **Marketing** message type. ![Message type cards with Marketing message selected](https://docs.pushwoosh.com/emails-send-email-28.webp) 4. If the sunset policy is on, scroll to **Delivery settings** and turn on [**Ignore sunset policy**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#ignore-the-sunset-policy). ![Ignore sunset policy switch turned on in the Delivery settings of the Email element](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-7.webp) 5. Click **Save**. Caution Don’t skip **Ignore sunset policy** when the policy is on. The segment also includes contacts who are already past the policy period. Without the switch, the policy skips the email for these contacts, but the journey still moves them on. They can’t click a link they never received, so they reach the **Not triggered** branch and get unsubscribed without being asked. A click on the confirmation link also counts as activity for the sunset policy, so contacts who reconfirm keep getting your marketing emails. ### Wait for the confirmation click 1. After the Email element, add a [Wait for Trigger element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/). 2. In **Waiting period**, set the time to wait for the click, for example, 3 days. ![Waiting period of 3 days in the Wait for Trigger element](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-3.webp) 3. In **Branch Name**, enter **Reconfirmed**. 4. Click **Add Event** and choose **PW_EmailLinkClicked**. ![Reconfirmed branch with the PW_EmailLinkClicked event](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-6.webp) 5. Click **Save**. What happens after the waiting period starts: * **Any link reconfirms.** Any link in the email fires **PW_EmailLinkClicked**, so a click on any link reconfirms the contact. * **Scanners don’t count.** Clicks from email security scanners don’t fire the event, so they don’t reconfirm anyone. * **No click means Not triggered.** Contacts who don’t click within the waiting period go to the **Not triggered** branch, which is created automatically. Count only the confirmation link (optional) To count only the confirmation link, click **Add condition**, choose **redirect_link** and **contains**, and enter the confirmation URL. ### Unsubscribe contacts who don’t reconfirm 1. On the **Not triggered** branch, add an **Update User Profile** element. 2. In **Manual Tag Value**, choose the **Unsubscribed Emails** tag and set the value to **true**. ![Update User Profile element setting the Unsubscribed Emails tag to true](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-4.webp) 3. Click **Save**. What this step does: * **Marketing emails stop.** The **Unsubscribed Emails** tag stops marketing emails to the contact. Transactional emails still reach them. * **You can undo it.** To subscribe a contact again, set the tag back to **false**. * **Contacts who already unsubscribed enter the journey too.** The segment doesn’t check the tag. These contacts don’t receive the reconfirmation email, so they end up on this branch. Their tag stays **true**. Keep inactive contacts subscribed Skip this element if you’d rather keep inactive contacts subscribed. If the sunset policy is on, it stops marketing emails to them once they pass the policy period. ### End the branches and launch 1. On the **Reconfirmed** branch, optionally send a thank-you email. 2. End both branches with an [Exit element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or leave them without an outgoing connection. ![Journey canvas: Audience-based Entry No opens or clicks for 150 days, Reconfirmation email, Wait for Trigger element Wait for click with the Reconfirmed branch ending in Exit and the Not triggered branch leading to Update User Profile Unsubscribe and Exit](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-email-sunset-flow-8.webp) 3. Click **Launch campaign**. --- # How to set up an abandoned cart campaign **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/ Recover abandoned carts with an email that shows left-behind items, triggered by PW_AbandonedCart in a Customer Journey. --- Shoppers who add items to a cart and leave without buying are some of the easiest conversions to recover. They’ve already shown intent. This guide shows how to send them an abandoned cart email in Pushwoosh: fire the `PW_AbandonedCart` event from your store, show the left-behind items in the email with the [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block, and trigger the send from a Customer Journey. ## What is PW_AbandonedCart `PW_AbandonedCart` marks that a user left items in their cart without buying. It is not a built-in [default event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/). It is a shared event name used across Pushwoosh e-commerce integrations so journeys, the Products block, and segments all work the same way. * **[Magento integration](https://docs.pushwoosh.com/product/integrations/magento-integration/):** sends `PW_AbandonedCart` and `PW_OrderCreated` for you automatically. * **[Shopify integration](https://docs.pushwoosh.com/product/integrations/shopify/):** sends `PW_AbandonedCart`, `PW_OrderCreated`, and `PW_AbandonedCartUpdate` (fires again as the shopper keeps editing the same checkout) for you automatically, with cart items enriched from your Product Catalog. * **[WooCommerce integration](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/):** sends `PW_AbandonedCart`, `PW_AbandonedCartUpdate` (fires when the shopper changes the same cart after `PW_AbandonedCart` and leaves it again), and `PW_OrderCreated` for you automatically, with a link that restores the cart. * **Any other store or custom backend:** your team sends both events via [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) with the names and attributes in [How to send abandoned cart reminders](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#how-to-send-abandoned-cart-reminders). ### What happens when you send PW_AbandonedCart Sending `PW_AbandonedCart` does two things on the Pushwoosh side: 1. It’s recorded like any other custom event, so you can see it in [Event statistics](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) and use it in [segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-events/). 2. Pushwoosh stores the cart contents and tags the user with: * **PW_AbandonedCartID:** an identifier for the current cart, used to fetch its contents at send time. * **PW_AbandonedCartDate:** the `abandonedDate` value from the event payload. ### What happens when you send PW_OrderCreated When the shopper places an order, send `PW_OrderCreated` (or `PW_OrderUpdated`). Pushwoosh clears `PW_AbandonedCartID` and `PW_AbandonedCartDate` for that user right away. It does not match `orderId` values: any order from the user removes both tags, even if the purchase was for a different cart. **Example:** A shopper abandons cart `ORDER-100`, then buys `ORDER-200` before the reminder goes out. Pushwoosh clears the tags, so they won’t get a reminder about `ORDER-100`. What appears in the reminder email The Products block reads the cart stored on the user profile at send time. It does not use the item list from the `PW_AbandonedCart` event that started the journey. * **Same `orderId`:** each new `PW_AbandonedCart` overwrites the stored cart. The reminder shows the latest items. * **Different `orderId`:** Pushwoosh stores a separate cart. Items are not merged. ## How to send abandoned cart reminders Follow these steps to create the events, send them from your store, build the email, and launch the Customer Journey. ### Create the events in your Control Panel [Pushwoosh auto-creates](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#1-create-events-in-pushwoosh-control-panel) an event and its attributes from the first `postEvent` call under a name it doesn’t recognize, inferring each attribute’s type from the value sent. That inference is unreliable for money fields in `PW_AbandonedCart`. Declare both events explicitly so the types are set correctly: 1. Go to **Audience → Events** in your Pushwoosh Control Panel and click **Create Event**. 2. Create a custom event named `PW_AbandonedCart` with these attributes: | Attribute | Type | | :--------------- | :-------------- | | `email` | string | | `orderId` | string | | `customerId` | string | | `orderUrl` | string | | `abandonedDate` | date | | `createdDate` | date | | `totalPrice` | decimal (price) | | `shippingAmount` | decimal (price) | | `taxAmount` | decimal (price) | | `discountAmount` | decimal (price) | | `currency` | string | 3. Repeat for `PW_OrderCreated` with a single `orderId` attribute. Use the correct attribute types Pushwoosh stores each attribute value only in the type set for it, whether declared here or inferred for an auto-created attribute. A value that doesn’t match the type is dropped silently, and `postEvent` still returns success, so you won’t see in the response that data was lost. You can fix a wrong type later by editing the event in the Control Panel, but any data sent before the fix is already gone. For abandoned cart to work, match the types in the table above: * **Money fields** (`totalPrice`, `shippingAmount`, `taxAmount`, `discountAmount`): use **decimal (price)**. Left undeclared, a whole-number value like `129` infers as **integer**. Declared as **string**, the cart is not stored and no tags are set. Declared as **integer**, `129.90` becomes `129`. * **Dates** (`abandonedDate`, `createdDate`): use **date**. * **`items`:** do not add it to the event definition. Send it in the JSON payload as an array of objects. Use real JSON numbers for `price` and `quantity`, not quoted strings. [Learn more about creating custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/#creating-custom-events). ### Send the events from your backend Developer assistance needed You’ll need your development team’s help to send events from your store or backend. Please share this guide with them. Ask your development team to call [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) whenever a cart is abandoned, using this attribute schema: ```json { "request": { "hwid": "8f65b16df378e7a6bece9614e1530fb2", "application": "XXXXX-XXXXX", "event": "PW_AbandonedCart", "attributes": { "email": "shopper@example.com", "orderId": "ORDER-10293", "customerId": "CUST-4471", "orderUrl": "https://yourstore.com/cart/restore/ORDER-10293", "abandonedDate": "2026-07-28T10:15:00Z", "createdDate": "2026-07-28T09:40:00Z", "totalPrice": 129.90, "shippingAmount": 9.90, "taxAmount": 0, "discountAmount": 0, "currency": "USD", "items": [ { "productId": "SKU-4821", "name": "Wireless headphones", "quantity": 1, "price": 119.90, "sku": "SKU-4821", "description": "Over-ear, noise-cancelling", "imageUrl": "https://yourstore.com/media/sku-4821.jpg", "productUrl": "https://yourstore.com/products/sku-4821", "category": "Audio" } ] }, "userId": "shopper@example.com" } } ``` When the cart converts into an order, send `PW_OrderCreated` with just the `orderId` to clear the cart tags: ```json { "request": { "hwid": "8f65b16df378e7a6bece9614e1530fb2", "application": "XXXXX-XXXXX", "event": "PW_OrderCreated", "attributes": { "orderId": "ORDER-10293" }, "userId": "shopper@example.com" } } ``` Tip If you use the [Magento integration](https://docs.pushwoosh.com/product/integrations/magento-integration/#abandoned-cart-sync-configuration) or the [Shopify integration](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/#order-paid-and-checkout-events), you don’t need to send these events from your backend. Magento sends `PW_AbandonedCart` and `PW_OrderCreated` for you on a schedule; Shopify sends them (plus `PW_AbandonedCartUpdate` for in-progress checkout edits) from webhooks in real time. The [WooCommerce integration](https://docs.pushwoosh.com/product/integrations/woocommerce-integration/#abandoned-cart-events) sends `PW_AbandonedCart` for you after checking carts every 15 minutes, and `PW_OrderCreated` when the order is placed. ### Add a Products block to your email 1. Open the [email content editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/) and add a [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block to the canvas. 2. In the settings panel, set **Source** to [Cart](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#show-items-from-the-cart). Note The Products block **Cart** source needs **Content Templates** on your plan and an [API access token](https://docs.pushwoosh.com/developer/api-reference/api-access-token/). See [Products block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products). 3. Set **Max products shown** to limit how many cart items appear in the email. ![Products block settings with Source set to Cart and Max products shown](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-an-abandoned-cart-campaign-1.webp) 4. Style the grid and card template as needed. See [Style the grid](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#style-the-grid). ![Products block on the email canvas with card template and grid style settings](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-an-abandoned-cart-campaign-2.webp) When the email sends, the Products block looks up each recipient’s stored cart using their `PW_AbandonedCartID` tag. It then fills the block with the current cart items: image, title, price, and a link back to the cart. The layout comes from the card template you set in the editor. Recipients without that tag (nothing abandoned, or already ordered) skip the block instead of showing empty content, so it’s safe to use in a broader campaign too. Tip The Cart source is available in the email content editor. To remind shoppers with a push instead, use a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) step with [personalization](https://docs.pushwoosh.com/product/personalization/dynamic-content/) that references the cart details from the event. ### Build the Customer Journey 1. Add a [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) using the `PW_AbandonedCart` event. 2. Add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step. Set its waiting period to an hour or two. Name the branch **Order placed** and add the `PW_OrderCreated` event to it. Caution Set the waiting period on **Wait for Trigger** itself. Do not add a separate **Time Delay** step before it. **Wait for Trigger** only starts listening for `PW_OrderCreated` once the shopper reaches that step. If **Time Delay** comes first and the shopper places an order during that delay, the order event goes undetected. The shopper moves to the **Not triggered** branch, but `PW_OrderCreated` has already cleared `PW_AbandonedCartID`. The Products block finds no cart and skips it. The shopper still receives the abandoned cart email with no products, even though they already completed their purchase. 3. Route the **Order placed** branch to [Exit](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/). The user already bought, so don’t remind them. 4. Route the **Not triggered** branch to the [Email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) step. Save the email you built in [Add a Products block to your email](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#add-a-products-block-to-your-email) as [email content](https://docs.pushwoosh.com/product/content/email-content/), then select it as this step’s content. ![Customer Journey canvas with Trigger-based Entry, Wait for Trigger with Order placed and Not triggered branches, Email and Exit steps, and no Time Delay](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-an-abandoned-cart-campaign-3.webp) ### Test before you launch 1. Send a test `PW_AbandonedCart` event for a test device. Don’t set the `PW_AbandonedCartID` tag manually: the Products block loads cart contents from the stored cart that the event creates, not from the tag value. Without a real event, the block renders empty. 2. Preview the email and confirm the layout and product content before sending to real shoppers. --- # How to set up product recommendations in email **URL:** https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-product-recommendations-in-email/ Send events that feed the Products block's Bestsellers, Bought together, and Based on what they viewed strategies in Pushwoosh. --- Setting the Products block’s **Source** to **Recommendations** can rank products by **Bestsellers**, **Back in stock**, **Price drop**, **New arrivals**, **Bought together**, or **Based on what they viewed**, without you picking products or writing a catalog rule. See [Get recommended products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products) for every field this source adds. Three of those strategies need data from outside your catalog to work: **Bestsellers** reads your order history, and **Bought together** and **Based on what they viewed** read what shoppers have looked at and bought. This guide covers what to send so each strategy has something to rank. Tip Already sending `PW_AbandonedCart` and `PW_OrderCreated` for [abandoned cart recovery](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/)? Keep reading. **Bestsellers** needs one more field on `PW_OrderCreated` that the abandoned cart guide doesn’t cover. ## Before you begin Make sure your account’s catalog has products in it. Go to **Content → Product Catalog** and connect a feed, import a CSV, or add products by hand. [Learn how to populate your catalog](https://docs.pushwoosh.com/product/content/product-catalog/#ways-to-populate-your-catalog). Developer assistance needed Sending the events below requires your development team’s help, unless your store integration already sends them for you. Please share this guide with them. New accounts see the regular catalog first Every strategy below needs history to accumulate before it can rank anything. Until then, the block shows the regular catalog instead. See the note under [Get recommended products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products). ## Which strategies need events Three of the six strategies rank products from events you send; the other three rank directly off your catalog and need nothing from you. | Strategy | Needs events? | Data source | | :---------------------------- | :------------ | :-------------------------------------------------------------------------- | | **Back in stock** | No | Catalog stock changes | | **Price drop** | No | Catalog price changes | | **New arrivals** | No | Catalog “added” date | | **Bestsellers** (7/30 days) | Yes | `PW_OrderCreated` / `PW_OrderUpdated` with `items` | | **Based on what they viewed** | Yes | Any event carrying a product ID. Order `items` also feed the pairs it ranks | | **Bought together** | Yes | Any event carrying a product ID or order `items`, plus a device tag | A stock or price change in your feed, or the next scheduled sync, updates **Back in stock**, **Price drop**, and **New arrivals** automatically. Nothing to send for those three. ## Bestsellers: add items to your order events **Bestsellers (7 days)** and **Bestsellers (30 days)** rank products by how many units sold in that window. They read the `items` array on `PW_OrderCreated` and `PW_OrderUpdated`, specifically each item’s `productId` and `quantity`. If you already send `PW_OrderCreated` to [clear abandoned cart tags](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#what-happens-when-you-send-pw_ordercreated), that minimal call (just `orderId`) still clears the cart, but it gives Bestsellers nothing to count. Add the order’s line items to the same call: ```json { "request": { "hwid": "8f65b16df378e7a6bece9614e1530fb2", "application": "XXXXX-XXXXX", "event": "PW_OrderCreated", "attributes": { "orderId": "ORDER-10293", "items": [ { "productId": "SKU-4821", "quantity": 1 }, { "productId": "SKU-5190", "quantity": 2 } ] }, "userId": "shopper@example.com" } } ``` `productId` must match the ID your [Product Catalog](https://docs.pushwoosh.com/product/content/product-catalog/) uses for that item, so Bestsellers can look up the product to show. Extra item fields (price, name, and so on) are ignored for this strategy. Only `productId` and `quantity` count toward the ranking. The event also needs `orderId`, because an order without it isn’t counted. When an order changes, send `PW_OrderUpdated` with the same `orderId` and `items`. For each product in the event, the new `quantity` replaces the earlier one instead of adding to it. A product can go down to 1 unit this way, but not to zero: * **`quantity` of 0 or below:** the product counts as 1 unit sold. * **Product left out of `items`:** the product keeps the quantity from the last event that included it. For example, a customer orders 2 units of a product and returns both. If you send `PW_OrderUpdated` with that product’s `quantity` set to 0, Bestsellers counts 1 unit for it instead of 2. If you drop the product from `items`, it still counts 2. No event removes a returned or cancelled product from Bestsellers. It stops counting only once the order falls outside the 7- or 30-day window. Paid Shopify orders already count If you use the [Shopify integration](https://docs.pushwoosh.com/product/integrations/shopify/): * Every paid order arrives as `PW_OrderCreated`, with the Shopify order ID as `orderId` and `items` carrying `productId` and `quantity`. Paid orders count toward Bestsellers with nothing to add on your side. * `productId` is the Shopify product ID, the same ID your catalog uses when it’s imported from Shopify. * The integration doesn’t send `PW_OrderUpdated`, so Bestsellers counts the quantities from the original order and doesn’t reflect later edits, refunds, or cancellations. Send quantity as a number Use a real JSON number for `quantity`, not a quoted string. Pushwoosh stores each attribute value only in the type it was first declared or inferred with; a mismatched value is dropped silently, and `postEvent` still returns success. See [Use the correct attribute types](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/#create-the-events-in-your-control-panel) for the same caution on `PW_AbandonedCart`. **Bestsellers** ranks by order history specifically. Views and cart activity don’t count. For a ranking that includes browsing and cart activity too, use **Based on what they viewed** or **Bought together** instead. ## Based on what they viewed and Bought together: track product activity Both strategies build from the same signal: events that carry a product ID. You don’t need a dedicated “product viewed” event name — any [custom event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) sent via [postEvent](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) counts, as long as its `attributes` include one of these keys: * A single product, as a top-level attribute: `product_id`, `productId`, `productid`, `item_id`, or `sku`. * Multiple products, as a `products` array where each item has `product_id`, `productId`, `id`, or `sku`. For example, fire your existing product-view event with a product ID attribute already on it: ```json { "request": { "hwid": "8f65b16df378e7a6bece9614e1530fb2", "application": "XXXXX-XXXXX", "event": "ProductViewed", "attributes": { "product_id": "SKU-4821", "category": "Audio" }, "userId": "shopper@example.com" } } ``` **Based on what they viewed** recommends products that go with what each shopper looked at recently. It works like this: * Pushwoosh keeps the last 10 products each shopper sent events for, newest first. Only events with the product ID keys above add products to this list. Order `items` don’t. * The block shows products paired with those recent products, using the same pairs as **Bought together** (see below). Orders count toward those pairs, so sending `PW_OrderCreated` with `items` improves this strategy too, but it isn’t required. * Products the shopper already has in their recent list aren’t shown. * The list is cleared 30 days after the shopper’s last product event. If a shopper has no recent products, or none of them has pairs yet, the block shows the regular catalog for them. No product tag or other setup is needed. Tip If you use the [Shopify integration](https://docs.pushwoosh.com/product/integrations/shopify/), the storefront theme embed already sends `PW_ProductViewed` with a `productId` on every product page view — nothing to add on your side, as long as the **Push Init Embed** toggle is on (it’s off by default). See [Storefront browsing events](https://docs.pushwoosh.com/product/integrations/shopify/events-and-campaigns/#storefront-browsing-events). **Bought together** shows products that shoppers often pick alongside a given product. For example, for a phone, the block can show the cases and chargers that other phone shoppers also viewed or bought. The more shoppers interacted with both products, the higher the recommended one ranks. Despite the name, purchases aren’t required. Views, cart events, and orders all count. The strategy reads: * Every event that carries a product ID, using the keys listed above. * The `items` array of `PW_OrderCreated` and `PW_OrderUpdated`. Other events’ `items`, including `PW_AbandonedCart`, aren’t read. Pushwoosh pairs products like this: * Two products form a pair when the same user sent events for both within the last 30 days. The events can be separate: the products don’t need to be in the same order. * Pairs are counted within one application. Activity in your account’s other applications doesn’t count. * The rankings are recalculated once a day, so new events affect them starting the next day. **Bought together** needs one more thing: a **Product tag**, a [device tag](https://docs.pushwoosh.com/developer/api-reference/tags/) that holds the ID of the product to build recommendations for. Set the field’s name when you configure the strategy in the [Products block](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products) (for example `PW_LastViewedProductID`), then keep that tag updated on each device. For example, [set it](https://docs.pushwoosh.com/developer/api-reference/tags/) to the product’s ID whenever the shopper views a product. Without the tag set on a recipient, the block falls back to the regular catalog for them. Tag name rules The tag name only accepts letters, digits, underscores, and spaces, since it has to be addressable in Liquid. See [Bought together needs a Product tag](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products). ## Confirm your events are landing Before you add the block, check that Pushwoosh is actually receiving the events above: go to **Audience → Events**, open the event you sent (`PW_OrderCreated` or the custom event carrying a product ID), and confirm recent hits show up. See [Event statistics](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/). Note An empty Products block can mean either “the events never arrived” or “not enough history has accumulated yet” (see the note above). Event statistics rules out the first cause before you chase the second. ## Add the block to your email Add a [Products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#products) block to your [email content](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/), set **Source** to **Recommendations**, and pick a **Strategy**. See [Get recommended products](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/blocks/#get-recommended-products) for every field in the settings panel. Click **Refresh preview** to check the canvas fills with real products before you send. --- # Campaign basics **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/ Learn campaign types in Pushwoosh, then send segmented or transactional messages, schedule delivery, and run A/B/n tests. --- In Pushwoosh you build campaigns in **Customer Journey Builder**. Most setups use one of three entry patterns: * **[Audience-based (segmented)](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/):** you pick a segment and send now or on a schedule. Typical for promotions, seasonal offers, and reactivation. * **[Trigger-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/):** a journey starts when a user event fires in the app or on the site, including one your backend sends with [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent). Typical for welcome flows, abandoned cart, reminders, and transactional messages such as order confirmations or password resets. See [How to send transactional messages](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-transactional-messages/). * **[API-based](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/):** your backend calls a dedicated API-based entry endpoint to launch the journey directly, with segmentation conditions and content in the request itself and no Event involved. Typical for business changes that happen outside any event stream, like a price drop or an item back in stock. You can also schedule delivery for a chosen time and run A/B/n tests on message, timing, or channel inside a journey. Use the table below to compare segmented and triggered campaigns, then open a guide for the flow you need. ## Choosing the right campaign type for your goals | | Segmented campaign | Triggered campaign | | :------------------------- | :------------------------------------------------------------------------------------------------------ | :------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | How it works | Sent to a user segment right away or as scheduled. | Sent to individual users as soon as a specific event occurs. | | Where the data comes from | User attributes (tags), user events, and data synced from third-party tools (e.g., Amplitude, Piano). | User behavior is collected and stored in Pushwoosh. | | How it starts | Audience-based entry: targets a specific user segment. | Trigger-based entry: activated by a user event, including ones your backend sends via `postEvent`. | | Preparation | Define user segment, create content, and launch the campaign. | Create content and schedule a campaign before an event occurs. | | How it ends | Users exit the campaign (user journey) after all messages are sent, including follow-ups. | Users exit the campaign (user journey) one by one, depending on their behavior. | | How the data is calculated | As soon as all users from a segment exit the campaign, you can access opens, clicks, and other metrics. | Metrics are updated in real time, displaying user behavior. | | Use case scenarios | Marketing promotions & seasonal campaigns, reactivation, and retention campaigns for inactive users. | Welcome campaigns (event: user registration), abandoned cart (event: item added to cart), upselling campaigns (event: didn’t buy), reminders (event: made an appointment). | ## Pick a guide Open the guide for the campaign you want to send. [Send a segmented campaign ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-segmented-campaign) [Send a triggered campaign ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-triggered-campaign) [Send transactional messages ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-transactional-messages) [Send a message with a fallback channel ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-rich-message-with-a-fallback-channel) [Schedule a message ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message) [Run A/B/n tests in journeys ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-abn-test-customer-journeys) --- # How to run A/B/n tests in customer journeys **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-abn-test-customer-journeys/ Learn how to test different message variations in Pushwoosh Customer Journey Builder to optimize campaign performance and increase conversions. --- ## Why A/B/n testing is useful It’s not always obvious which message, offer, timing, or channel will get the best response from your users. A/B/n testing lets you compare several variations at once so you can: * Find which combination drives the most conversions * Test ideas quickly in one journey instead of running separate campaigns * Keep a control group to benchmark against your current communication For example, an online store can test whether more users complete checkout when they receive: a push with a 10% discount right away, an email with free shipping after 24 hours, or an SMS reminder with no incentive. ## What you can test with A/B/n split in Pushwoosh The [A/B/n split element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) in Customer Journeys allows you to test many aspects of your campaigns. Common test cases include: * **Timing**: Compare immediate vs. delayed messages (e.g., cart reminder right away vs. after 24 hours). * **Personalization**: Test generic vs. personalized content (e.g., “Shop new arrivals” vs. “Anna, your saved items are back”). * **Channel performance**: See which channel works best. * **Message content**: Experiment with different formats, including copy, visuals, call-to-action buttons, subject lines, layouts, etc. Below, we’ll cover some common cases where A/B/n testing can help optimize your customer journeys. ## Testing discount types for abandoned carts #### Goal Find out which discount type drives the highest conversion among users who abandoned their cart: * 10% off * $10 off * Free shipping ### Step 1. Set up the entry element Start the journey with a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element and select the event trigger `Cart_Abandoned`. This ensures the journey targets users who added items to their cart but didn’t complete checkout. Optionally, you can filter the audience to include users who abandoned a cart with a minimum value (e.g., $50+). > For more information on how to configure events, see [Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) documentation. ![Trigger-based entry configured with Cart_Abandoned event and optional $50+ cart value filter](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-1.webp) ### Step 2. Define the Conversion goal Set a Conversion goal to measure the test results. For this, click [Conversion goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) at the top of the canvas and set the target event to `CheckoutSuccess`. This way, you can see which discount offer results in more completed checkouts. ![Conversion goal element with CheckoutSuccess selected as the journey’s success metric.](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-2.webp) ### Step 3. Configure the A/B/n split element After the entry element, place an [A/B/n split element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) to divide users into three groups. Distribute users evenly across branches. Choose **Winner is selected automatically by goal** and select `CheckoutSuccess` as the goal for winner selection. This means once Pushwoosh detects a statistically significant winner, weaker branches will be deactivated, and all new users will be routed to the winning branch. ![A/B/n split with three branches](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-3.webp) ### Step 4. Configure the rest of the journey Set up the message in each branch. For each branch, add and configure a [push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/). For example: * **Branch A (control):** Send a push offering 10% off.\ *Example content:* “Complete your order today and enjoy 10% off – limited time only!” * **Branch B:** Send a push offering $10 off.\ *Example content:* “Here’s $10 off your order – finish checkout now.” * **Branch C:** Send a push offering free shipping.\ *Example content:* “We’ve saved your cart! Order today and get free shipping.” End each branch here — add an [Exit element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or leave it without an outgoing connection.\ ![Three discount message branches configured](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-4.webp) ### Step 5. Launch the journey Once all branches are configured, launch the journey. Users who abandoned a cart will be randomly split into three groups, each receiving its assigned discount offer. When statistical significance is reached, Pushwoosh will automatically direct all new users with an abandoned cart to the winning branch. To analyze the results, double-click the A/B/n split to open detailed statistics. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#analyze-the-results) ## Testing channel effectiveness for a fitness app re-engagement campaign #### Goal Determine which [communication channel](https://docs.pushwoosh.com/product/messaging-channels/) (push, email, SMS) best motivates inactive users to return to the app. Important To run this test correctly, ensure that each user is reachable via push notifications, email, and SMS, and that all corresponding identifiers (device tokens, email addresses, phone numbers) are [linked under a single custom User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/omnichannel-campaigns/#linking-devices-for-omnichannel-campaigns). This allows Pushwoosh to accurately identify the same user across channels, deliver messages properly, and correctly distribute users across test branches. This setup requires developer assistance. Share this guide with your development team: [Set a custom User ID and link it to multiple devices](https://docs.pushwoosh.com/developer/pushwoosh-knowledge-hub/users-userids/). ### Step 1. Add an entry element and choose the audience Start the journey with [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/) and select or create the segment **Inactive users** (no workout logged in 14+ days). > Learn more about [creating segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments). ![Audience-based entry targeting segment ‘Inactive users’](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-5.webp) ### Step 2. Define the Conversion Goal Set the `WorkoutLogged` event as the [Conversion Goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#conversion-goals) to measure which channel drives re-engagement. > For more information on how to configure events, see [Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) documentation. ![Conversion goal set to WorkoutLogged](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-6.webp) ### Step 3. Set up the A/B/n split element Add an [A/B/n split element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) after the entry element. Select 3 branches with even distribution and use **Winner is selected automatically by goal** with the chosen Conversion Goal.\ ![A/B/n split comparing push, email, and SMS with automatic winner by goal.](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-7.webp) ### Step 4. Configure the rest of the journey Configure the journey for the following branches. In each branch, add and configure the appropriate message element (Push, Email, or SMS): * **Branch A (control):** [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/)\ *Example content:* “It’s time to get moving 💪 Tap to start your workout now!” * **Branch B:** [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/)\ *Example content:* “Your fitness journey is waiting. Log your next session today!” * **Branch C:** [SMS element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/)\ *Example content:* “Don’t stop now! Track today’s workout and keep your streak alive!” End each branch here — add an [Exit element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or leave it without an outgoing connection. ![Configured journey: push copy, email subject, and SMS copy](https://docs.pushwoosh.com/how-to-abn-test-customer-journeys-8.webp) ### Step 5. Launch the journey Launch the journey. Users will be split across branches and contacted through one of the channels. Pushwoosh will automatically detect the winner and direct users to the winning branch. To analyze the results, double-click the A/B/n split to open detailed statistics. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/#analyze-the-results) --- # How to schedule a message **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message/ Learn how to schedule messages across multiple channels, including push notifications, emails, in-app messages, WhatsApp, and SMS. --- Pushwoosh makes it simple to schedule messages across multiple channels, including [push notifications](https://docs.pushwoosh.com/product/messaging-channels/push-notifications), [emails](https://docs.pushwoosh.com/product/messaging-channels/emails), [in-app messages](https://docs.pushwoosh.com/product/messaging-channels/in-apps), [WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages), and [SMS](https://docs.pushwoosh.com/product/messaging-channels/sms). Whether you’re sending a one-time message, scheduling messages for specific dates, or setting up recurring campaigns, the Customer Journey Builder offers flexible options to manage your campaign timing effectively. For push notifications, you can also use the [one-time push message form](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/send-push-notifications/one-time-push/). For one-time emails, you can use the [one-time email form](https://docs.pushwoosh.com/product/messaging-channels/emails/sending-emails/send-one-time-emails). This guide provides step-by-step instructions for scheduling messages through the Customer Journey Builder, applicable to all supported messaging channels. ## How to schedule messages that start a communication flow Scheduling is configured in the [**Audience-based entry**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#entry-schedule) element via **Entry schedule**: * **One-time entry:** users enter the journey once. Choose **Immediately** (as soon as the journey is activated) or **Schedule for a specific day** (date and time). * **Recurring entry:** users enter repeatedly. Choose **On specific dates** (multiple dates you pick) or **Periodically** (e.g. every 1 day, every 1 week). ### Configure the entry schedule 1. Add the **Audience-based entry** element and define the users who will enter the journey. Select the **Audience source** to determine who will receive the message or [create a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/). Once the audience is set, you can proceed to schedule the message. 2. In the **Audience-based entry** element, set **Entry schedule** and choose **One-time entry** or **Recurring entry**, then pick the option that matches when the message should be sent. 3. Set the time and timezone as described in the sections below. For details, see [Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/). Tip Optionally, enable **Allow users who match the segment later to enter the journey** so users who qualify after launch enter automatically. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#add-users-when-they-start-matching-the-segment-later). ### Schedule a message for a specific date and time To send a message on a specific date and time: 1. Set **Entry schedule** to **One-time entry**, then choose **Schedule for a specific day**. Set the date and time. 2. Select the timezone: * **Subscriber’s device timezone (default):** entry uses each subscriber’s device timezone. If timezone is missing, a fallback is used. Click **Change fallback** to change it. * **Specific timezone:** pick a timezone from the dropdown for a fixed entry time. Note If a subscriber’s scheduled entry time (in their local timezone) has already passed, they will not be included in the campaign unless you choose to let them enter immediately. [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#schedule-for-a-specific-day). Once the schedule is set, click **Apply** to confirm. ![Audience-based entry: Schedule for a specific day with date, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-2.webp) ### Schedule a message for multiple specific dates To send messages on multiple specific dates: 1. Set **Entry schedule** to **Recurring entry**, then choose **On specific dates**. 2. Pick the dates in the calendar. Click **+ Add Date** to add more dates. 3. Set the time and timezone the same way as for [Schedule for a specific day](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message/#schedule-a-message-for-a-specific-date-and-time) (Subscriber’s device timezone or Specific timezone). ![Audience-based entry: Recurring entry on specific dates](https://docs.pushwoosh.com/journey-elements-audience-based-entry-5.webp) Note Subscribers whose scheduled time has already passed for a specific date will be included in the next available scheduled date. Once the schedule is set, click **Apply** to confirm. ### Set up a recurring message to be sent at regular intervals To send messages at regular intervals (e.g. daily, weekly): 1. Set **Entry schedule** to **Recurring entry**, then choose **Periodically**. 2. In **ENTRY EVERY**, set the interval (e.g. every 1 day, every 1 week) and pick the days (of the week or month, depending on the interval). 3. Set the time and timezone the same way as for [Schedule for a specific date and time](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message/#schedule-a-message-for-a-specific-date-and-time). ![Audience-based entry: Recurring entry periodically with interval, selected days, time and timezone](https://docs.pushwoosh.com/journey-elements-audience-based-entry-6.webp) Note Subscribers whose scheduled time has already passed will be included on the next scheduled day. Once the schedule is set, click **Apply** to confirm. After you’ve set the scheduling options, add the message element and configure the rest of your journey. Once you’ve reviewed all the configurations, click **Launch campaign**. ## How to schedule messages that continue a communication flow If your scheduled message is part of an ongoing journey, you can insert a Time Delay element to control when the message is sent within the flow. For this: 1. Insert the **Time Delay** element between the preceding communication elements and the message you wish to schedule: [](https://docs.pushwoosh.com/shared-7.webm "Customer Journey Builder showing Time Delay element inserted between entry point and message") 2. Double-click the **Time Delay** element. In the **Time Delay Option** field, select either **Specific time**: ![Time Delay element configuration dialog with Specific time option](https://docs.pushwoosh.com/shared-9.webp) or **Date**: ![Time Delay element configuration dialog with Date option](https://docs.pushwoosh.com/shared-4.webp) Tip Within the **Time Delay** element, you can set other delivery conditions: waiting for fixed durations, specific days of the week, or until the user triggers a specified event. [Learn more about how to use the Time Delay element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/). ## Example scenario: Setting up a recurring push notification for a fitness app Let’s say you’re running a fitness app and want to send motivational push notifications to users to keep them engaged. You decide to send the same notification every Monday at 9:00 AM, reminding users to log their workout progress. 1. **Create a basic push flow.** Add the **Audience-based entry** to the journey canvas and select an **Audience source**. 2. **Set recurring entry.** In the **Audience-based entry** element, set **Entry schedule** to **Recurring entry**, then choose **Periodically**. In **ENTRY EVERY**, set **every 1 week** and select **Monday**. 3. **Set time and timezone.** Set the time to **9:00 AM** and select **Subscriber’s device timezone** so notifications are sent based on each user’s local time. 4. **Review and finalize the journey.** Proceed to configure the rest of your journey and create push notification content. Example message * **Title:** Keep Moving! 💪 * **Message:** Hey [First Name], log your workout and stay on track with your fitness goals. Let’s make this week count! 🏋️‍♂️ ![Fitness app push notification preview with title and message content](https://docs.pushwoosh.com/shared-32.webp) To end the journey here, add the [**Exit**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) element or simply leave the branch without an outgoing connection. Before launching the journey, review the entire setup to ensure everything is configured correctly. The recurring notification is now ready to remind users to log their workouts every Monday at 9:00 AM in their local timezones. --- # How to send a message with a fallback channel **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-rich-message-with-a-fallback-channel/ Send a WhatsApp message in a journey and move users whose message the provider rejects to an SMS or RCS fallback message. --- When you send a message on a channel such as WhatsApp, SMS, LINE, or Facebook Messenger, the channel’s provider can reject it, and then the user doesn’t get it. For example, WhatsApp rejects a message when Meta hasn’t approved your message template and the user hasn’t written to you in the last 24 hours. A fallback channel fixes this. In a journey, users whose message the provider rejected go down the **Rejected by provider** branch and get the same message in another channel. The branch is available in the elements of these four channels. For LINE and Facebook Messenger, see the [LINE](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/line/#branch-when-the-provider-rejects-the-message) and [Facebook Messenger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/facebook-messenger/#branch-when-the-provider-rejects-the-message) elements. This guide sets it up on WhatsApp with SMS as the fallback. In the example: * A delivery feedback request goes out on WhatsApp. * Users whose WhatsApp message is rejected get it as SMS, or as RCS if you send a [rich message](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/). ## Before you start * WhatsApp is [connected to the project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/whatsapp-configuration/). * An SMS provider is [connected to the project](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/sms-configuration/). For a rich message, it’s Twilio with a Messaging Service, so the project can send RCS. The SMS text is part of the RCS message, so you need this for the SMS fallback too. See [RCS messages through Twilio](https://docs.pushwoosh.com/product/messaging-channels/sms/rcs-messages/). * For a rich message: rich messages are turned on for your account. If you don’t see the **RCS**, **WhatsApp**, and **Messenger** tabs in **Content > Messenger presets**, contact your Pushwoosh account manager. * Users have their phone number registered for both WhatsApp and SMS under the same User ID. Both elements send to the numbers registered under the user’s User ID, so users without an SMS number don’t get the fallback. See [Import contacts](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/register-users/import-contacts/). ## Prepare the messages Send a plain message, or a rich message: a message with buttons that you build once and send on several channels. * Plain message On WhatsApp, you send a [template approved in your Meta account](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#use-a-pre-approved-template-from-your-meta-account) or a custom message, which reaches only users who wrote to you in the last 24 hours. You select it later in the WhatsApp element. For the fallback, create an SMS preset: 1. Go to **Content > Messenger presets** and open the **SMS** tab. 2. Click **Create SMS content** and select **Regular SMS**. 3. Name the preset, for example `Delivery feedback SMS`. 4. Enter the text, for example `Your order has been delivered. How did it go? Tell us at example.com/feedback`. See [SMS presets](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/). 5. Save the preset. You select it later in the SMS element. * Rich message 1. Go to **Content > Messenger presets** and open the **WhatsApp** tab. 2. Click **Create rich message** and name the message, for example `Delivery feedback`. 3. In **Type**, select **Quick reply**. 4. Enter the **Body** and add a button for each answer: **Great**, **It was OK**, and **Not good**. See [Quick reply](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#quick-reply). 5. Select **RCS** in the **Channels** strip. 6. In the **SMS fallback** block, enter **SMS text**, for example `Your order has been delivered. How did it go? Tell us at example.com/feedback`. Phones without RCS get this text. See [Set the SMS fallback](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#set-the-sms-fallback). 7. Check that the **RCS** and **WhatsApp** badges aren’t red, then save the message. Pushwoosh creates a copy of the message for each channel set up in the project: a WhatsApp copy and an SMS preset that carries the RCS copy. To reach users outside the 24-hour window on WhatsApp, [get the WhatsApp template approved](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/rich-messages/#get-the-whatsapp-template-approved). ## Send the message on WhatsApp 1. Go to **Campaigns > Customer Journey Builder**, click **Create campaign**, and build the journey up to the step where the message goes out. For entry options, see [Campaign types](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/). 2. Drag the **WhatsApp** element from the left panel onto the canvas and connect the previous step to it. 3. Double-click the element. 4. In the **Content** dropdown, select the message: your template or **Custom Message** for a plain message, `Delivery feedback` for the rich message. For the rich message, the settings look like this: ![WhatsApp element settings with the Delivery feedback rich message selected in Content and the rich message preset note](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-rich-message-with-a-fallback-channel-1.webp) 5. Select the message type, **Marketing** or **Transactional**. A journey with an event or API entry can’t be launched without it. See [Marketing vs. transactional messages](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). 6. Turn on **Branch when the provider rejects the message**. ![Branch when the provider rejects the message switch turned on in the WhatsApp element settings, with the hint below it](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-rich-message-with-a-fallback-channel-2.webp) 7. Click **Apply**. On the canvas, the element gets these branches: * A message with quick reply buttons, like the rich message, gets one branch for each button and **Not match** for users who don’t tap a button within 24 hours. * A message without buttons gets **Sent**. The element holds the user for up to 1 hour to catch a refusal. If [Split flow based on message read status](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages/send-whatsapp-messages/send-whatsapp-via-customer-journey/#split-the-flow-based-on-message-read-status-optional) is on, the element gets the split’s branches instead and waits for the time set in it. * **Rejected by provider**: users whose message WhatsApp refuses at sending, or whose later delivery report from Meta marks it as failed or undelivered. These users go to this branch at once. See [Branch when the provider rejects the message](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/#branch-when-the-provider-rejects-the-message). A user without a WhatsApp number doesn’t go to **Rejected by provider**, because the message isn’t sent at all. The user continues through **Not match** or **Sent** and doesn’t get the fallback. ## Send the fallback as SMS or RCS 1. Drag the **SMS** element onto the canvas. 2. Connect the **Rejected by provider** branch of the WhatsApp element to the SMS element. 3. Double-click the SMS element. 4. In **Preset**, select the SMS preset: `Delivery feedback SMS` for a plain message, or `Delivery feedback`, the preset with the same name that Pushwoosh created from the rich message. ![SMS element settings with the Delivery feedback preset selected and its SMS text in Preview content](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-rich-message-with-a-fallback-channel-4.webp) 5. Select the message type. 6. Click **Save**. For the rich message, RCS and SMS go through the same SMS element, depending on the phone: * Phones with RCS get the message with its buttons. * Other phones get the SMS text. The SMS element gets the same button branches as the WhatsApp element. **Not match** takes users who don’t tap a button within 24 hours and users whose phone has no RCS. A reply to the SMS text doesn’t choose a branch. Pushwoosh records it as the `PW_SMSReceived` event. ![Journey canvas with the WhatsApp element whose Rejected by provider branch leads to the SMS element, both with Great, It was OK, Not good, and Not match branches](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-rich-message-with-a-fallback-channel-3.webp) ## Finish the journey 1. Connect the branches of both elements to the next steps, for example **Not good** to a message from your support team. 2. Connect **Not match** or **Sent** to the next step or to **Exit**. 3. Click **Launch campaign**. With the rich message, the finished journey looks like this: ![Complete journey canvas: entry, WhatsApp element, SMS element connected by the Rejected by provider branch, Not good branches leading to a support message, and Not match branches leading to Exit](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-rich-message-with-a-fallback-channel-5.webp) For the details of each element, see [WhatsApp](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/whatsapp/) and [SMS](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/sms/). ## Check that it works The WhatsApp and SMS elements have no test send, so check the fallback on your own phone before you launch the journey for users: 1. Register your phone number for WhatsApp and SMS under one User ID. 2. Create a copy of the journey whose entry reaches only that User ID. 3. Don’t write to your WhatsApp business number for 24 hours, and send a custom message or a rich message whose WhatsApp template Meta hasn’t approved. Outside the 24-hour window, such a message is rejected. An approved template isn’t, so it doesn’t test the fallback. 4. Launch the copy. Your phone gets the SMS or RCS message instead of the WhatsApp one. --- # How to send a segmented campaign **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-segmented-campaign/ Sending your messages to a user segment with Pushwoosh --- Pushwoosh allows you to send messages and campaigns to a user segment. This helps you to personalize your campaigns. Use cases: * Send a message to a language segment: email in Hindi or mobile push in Portuguese. * Send a retention email with a country-specific or time-limited offer. In Pushwoosh, you can create two types of user segments: 1. Segments based on user attributes, e.g., the user’s country or language. 2. Segments based on user behavior, e.g., last login or previous purchases. Once you’ve added a user base to Pushwoosh, you can use [default tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#default-tags) and [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events) right away. Additionally, you can create your own [custom tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags#custom-tags) and [events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events). Note The [first_name] tag is a custom tag. You obtain users’ names during their registration and can then send this data to Pushwoosh. ## How to create a segmented campaign This process consists of three steps: **Step 1**: Create a user segment. [Follow this guide to create your user segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments). **Step 2**: Craft a message. [Mobile or web push](https://docs.pushwoosh.com/product/content/push-presets), [email](https://docs.pushwoosh.com/product/content/email-content), [in-app message](https://docs.pushwoosh.com/product/content/in-apps), [SMS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets), or [WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages). **Step 3**: Build the segment and the message into a campaign. ## How to build a campaign targeted at a user segment Once your segment and message(s) are ready, go to **Campaigns > Customer Journey Builder > Create campaign** and click **Build a journey from scratch**. ![Customer Journey Builder canvas showing the 'Build a journey from scratch' button and options for creating a new campaign](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-1.webp) Drag the **Audience-based Entry** into the canvas, as we are using it to send a message to a user segment. Double-click the Audience icon, then click the **Audience Source**. From the dropdown, choose the segment you want to use. Click **Apply**. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-2.webm "Audience-based Entry element dragged to canvas with Audience Source dropdown menu showing available user segments") Click **Connect this** to add a messaging channel. In this example, we are using an email to reach out to users who didn’t open our app in 7 days. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-3.webm "Customer Journey Builder showing the 'Connect this' option to add a messaging channel to the audience entry") Double-click your message and choose a preset — the message copy you have prepared for the campaign. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-4.webm "Message preset selection dropdown displaying available email templates for the segmented campaign") You can stop here and don’t send anything else — simply leave the message without an outgoing connection, or click the connecting dot icon and choose **Exit** if you want to mark the end explicitly. To launch your campaign, give it a name by clicking the three dots icon in the left upper corner > **Rename**. Once ready, click **Launch**. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-5.webm "Campaign naming interface with three dots menu open showing the 'Rename' option to name the campaign") ## How to send a follow-up message to a user segment You can automatically follow up with users who didn’t open your previous email or push notification. To do so, double-click the message icon (the email icon in our case) and toggle on [**Split to opened/ignored**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#split-flow-depending-on-whether-this-message-is-opened-or-ignored). [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-6.webm "Split to opened or ignored toggle option being enabled for the email message in Customer Journey Builder") Choose how many days to wait to check the opens. For the email, we chose five days, but depending on the case, you can choose to wait for 30 minutes. For example, to follow up with a user about their flight changes or another urgent update. Then click **Apply**. Now you see two options to automate: **opened**/**not opened**. If a user opened your email > then end this branch. Leave it without an outgoing connection, or click the connecting dot next to **opened** and choose **Exit** if you want to mark the end explicitly. If a user didn’t open your email, send them another email. To do so, click the connecting dot next to **not opened** and choose any messaging channel. In our case, we are following up with another email. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-7.webm "Messaging channel selection interface for the 'not opened' path showing option to add a follow-up email") Double-click your message and choose a preset—the message copy you have prepared for the campaign. [](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-segmented-campaign-8.webm "Follow-up message preset selection interface displaying templates for the second email in campaign automation") To stop the automation here, leave the message without an outgoing connection, or click the connecting dot icon and choose **Exit** if you want to mark the end explicitly. To launch your campaign, give it a name by clicking the three dots icon in the left upper corner > **Rename**. Once ready, click **Launch**. ## Popular combinations of follow-ups * Mobile push + email * Email + email * Mobile push + SMS * Email + SMS * Mobile push + WhatsApp * Email + SMS + WhatsApp ## How to schedule a segmented campaign You can launch your campaign once at a specific time (e.g., December 10 sale start). Or launch it periodically (e.g., every Friday at 5pm for game reminders). [Learn more about scheduling options](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-schedule-a-message/). --- # How to send a triggered campaign **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-triggered-campaign/ Set up an event-triggered campaign in Pushwoosh Customer Journey Builder to message users the moment they take an action. --- Pushwoosh lets you message users the moment they take a specific action, such as a signup or a purchase, instead of waiting for a scheduled send. Use cases: * Welcome a new user right after registration. * Thank a user after a purchase. * Remind a user when a custom event fires in your app or on your site. Unlike a [segmented campaign](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-segmented-campaign/), a triggered campaign doesn’t target a pre-built audience. It starts individually for each user, as soon as their event occurs. [See how segmented and triggered campaigns compare](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/#choosing-the-right-campaign-type-for-your-goals). This guide shows a simple path: **Trigger-based entry** → one message → **Launch**. If you need to wait and message only when the user skips a later action (for example, abandoned cart before purchase), add a [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step after entry, or follow [Set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/). For journeys started by your backend instead of an in-app event, use [API-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/). ## Before you build ### Event Your app or backend must send the event (SDK or `/postEvent`). Then pick one path: * **Default event:** use one already tracked by the Pushwoosh SDK, such as [`PW_InAppPurchase`](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events). * **Custom event, auto-created:** send the event under a new name. Pushwoosh [creates the event and its attributes](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#1-create-events-in-pushwoosh-control-panel) from the first call and infers attribute types from the values. * **Custom event, defined first:** go to **Audience > Events**, add the event and attribute types, then have your app or backend send it. Use this when you need control over attribute types instead of inference. Important Users enter only when the event includes a **User ID** or **HWID**, and the user has at least one registered device. Details: [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). ### Preset You need message content for the channel you will send. You can: * Create or reuse a preset under **Content** before you build: [Push](https://docs.pushwoosh.com/product/content/push-presets), [Email](https://docs.pushwoosh.com/product/content/email-content), [In-app](https://docs.pushwoosh.com/product/content/in-apps), [SMS](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets), or [WhatsApp](https://docs.pushwoosh.com/product/messaging-channels/whatsapp-messages). * Or create content from the channel step in the journey: open the Push (or other channel) element and use **Create content**. For push, see [Select or create push content](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#select-or-create-push-content). ## How to build a campaign triggered by a user event 1. Go to **Campaigns > Customer Journey Builder**. 2. Click **Create campaign**. 3. Click **Build a journey from scratch**. ![Create a campaign dialog with Build a journey from scratch and single-channel options](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-triggered-campaign-1.webp) 4. A **Trigger-based Entry** is already on the canvas when you create the campaign. Double-click it. 5. Choose the **Event** that should start the campaign. In this example, we use `SupportTicketClosed`, a custom event we defined first in **Audience > Events** (see [Before you build](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-a-triggered-campaign/#before-you-build)). 6. If your event is not in the list yet, click the link icon next to **EVENT** to open **Audience > Events**, [create the event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events/) (or wait until tracking has auto-created it), then return here and select it. Your app or backend still needs to send the event for users to enter. 7. Click **Save**. ![Trigger-based entry panel with Event set to SupportTicketClosed and Save](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-triggered-campaign-2.webp) Tip For this example, choosing the **Event** and clicking **Save** is enough. Use **Add condition** only if the event has attributes and you want only some values to start the campaign. For who can enter, availability windows, and re-entry rules, see [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). 8. Click **Connect this** on the entry, then choose **Push** as the messaging channel. The Push element appears on the canvas, connected to the entry. ![Trigger-based entry connected to a Push element on the canvas](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-triggered-campaign-3.webp) 9. Double-click the Push element. 10. Select a **preset**, or click **Create content** to make one in this step. [Learn more about creating push presets](https://docs.pushwoosh.com/product/content/push-presets/). 11. Click **Save**. ![Push step with the Support follow-up preset selected and its content shown in Preview content](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-triggered-campaign-4.webp) 12. For a one-message campaign, leave the message with no further step, or click the connecting dot on the message and choose **Exit**. Either way, the user leaves after that message. 13. Optional: click the three dots in the upper-left corner, then **Rename**, and name the campaign. 14. Click **Launch**. ![Renamed campaign with the Trigger-based Entry connected to the Push element, ready to launch](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-a-triggered-campaign-5.webp) This walkthrough is event → one message. It is not the full abandoned-cart recovery flow. ## How a triggered campaign behaves once it’s running * **Entry:** each user enters when their own event fires, not on a schedule. * **Exit:** each user finishes their own run through the campaign as they complete the steps. The whole campaign does not end for everyone at once. * **Metrics:** opens, clicks, and conversions update as users move through the campaign, rather than only after everyone has exited. If nobody enters after launch, the event may not be firing, entry conditions may be too narrow, or the user may have no registered device. Review the **Trigger-based Entry** settings and [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/). ## Common triggered campaign types * **Welcome and onboarding:** [How to onboard users](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) * **Abandoned cart** (wait for purchase, then message): [How to set up an abandoned cart campaign](https://docs.pushwoosh.com/tutorials/product-guides/email-marketing-campaigns/how-to-set-up-an-abandoned-cart-campaign/) * **Transactional messages** (order confirmations, password resets): [How to send transactional messages](https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-transactional-messages/) * **Re-engagement and reminders:** [How to create a re-engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh/) --- # How to send transactional messages **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-create-campaigns-in-pushwoosh/how-to-send-transactional-messages/ Learn how to automate transactional messages—such as order confirmations, password resets, and shipping updates—in Pushwoosh using Customer Journey Builder. --- Transactional messages, such as order confirmations, subscription renewals, password resets, shipping updates, or payment notifications, are essential for providing users with timely, relevant information based on specific actions. Pushwoosh allows you to automate these messages using the **Customer Journey Builder** tool. ## Prerequisites Developer assistance needed You’ll need assistance from your development team to complete this setup. Before setting up transactional messages, ensure that: * The [**Pushwoosh SDK**](https://docs.pushwoosh.com/developer/pushwoosh-sdk/pushwoosh-sdk-overview) is integrated into your mobile app or website. * The events you want to use in transactional journeys are created, set up, and tracked in Pushwoosh. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#implementation) Below are examples of common transactional messages and the steps to configure them using [**Customer Journey Builder**](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview). ## Sending an order confirmation To automatically send personalized order confirmation messages when a purchase event occurs, follow these steps: 1. In your Pushwoosh account, go to **Campaigns > Customer Journey Builder** and click **Create campaign** to start building the transactional message flow. 2. In the **Customer Journey Builder**, add the **Trigger-based entry element** to the canvas and select the [PW_InAppPurchase](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_inapppurchase) event (a default event). Ensure the event is properly configured. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events#pw_inapppurchase) Tip If a custom event is required for your app, create and set it up in advance. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/custom-events). ![Customer Journey Builder canvas showing Trigger-based entry element with PW_InAppPurchase event selected for order confirmation setup](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-1.webp) 3. Add a [**push notification**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element to the canvas. Create the message content, and use placeholders like `{order_id}` to personalize the message. ### Example push content **Title**: “Order Confirmation” **Message**: “Thank you for your order `{order_id}`! Your items will be shipped shortly.” ![Push notification content preview showing order confirmation message with order_id personalization placeholder](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-2.webp) 4. In the [Push element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#set-message-type), set **Message type** to **Transactional**. This is what actually makes the message [bypass user opt-outs and the control group](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/), not just frequency limits. A message left as Marketing still skips unsubscribed users even with frequency capping ignored. ![Message type selector on a channel element showing Marketing message (respects frequency capping and silence period, excludes control group) and Transactional message (bypasses frequency capping, sent to control group)](https://docs.pushwoosh.com/shared-62.webp) 5. To make sure the message is delivered regardless of any message limits, also select [**Send messages without global frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#ignore-global-frequency-capping) in the delivery settings. ![Delivery settings with Send messages without global frequency capping selected for transactional message](https://docs.pushwoosh.com/shared-53.webp) 6. **Finalize and launch the Journey**. Configure the message, and any optional segmentation or wait steps. End the branch with an [**Exit element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/exit/) or by leaving it without an outgoing connection, then click **Launch** to start the journey. ![Finalized Customer Journey with Exit element configured and ready to launch](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-3.webp) The transactional message will now be sent automatically when the trigger conditions are met. ## Sending a subscription renewal email By using third-party subscription management services such as [**Adapty**](https://docs.pushwoosh.com/product/integrations/adapty-integration), [**Apphud**](https://docs.apphud.com/docs/pushwoosh), etc., you can automatically send renewal notifications or emails to users when their subscription expires, prompting them to renew. For this, do the following: 1. First, configure the integration of the service with Pushwoosh. [Learn more about available integrations](https://docs.pushwoosh.com/product/integrations/#in-app-purchases) 2. Next, set up the **Subscription_expired** event in your chosen subscription management service. When a user’s subscription expires, the service will send a **subscription expired** event to Pushwoosh. 3. Once you have set up the event, in the **Customer Journey Builder**, click **Create campaign** and add the **Trigger-based entry element** to the canvas. 4. Select the **subscription expired** event sent from the third-party service. ![Trigger-based entry element configured with subscription expired event from third-party service](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-4.webp) 5. Add the [**email element**](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) to the canvas and select the email content you’ve created in advance. ### Example email content ***Subject**: “Your Subscription Has Expired – Renew Now to Stay Connected!”* ***Body:*** *Hi,* *We noticed that your subscription has expired, and we don’t want you to miss out on all the great benefits! Renew your subscription today to continue enjoying uninterrupted access to our services.* *[Renew My Subscription]* *Thank you for being a valued customer. We look forward to having you back!* 6. In the [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-message-type), set **Message type** to **Transactional**, then also select [**Send messages without global frequency capping**](https://docs.pushwoosh.com/product/messaging-channels/message-delivery-settings/frequency-capping/#ignore-global-frequency-capping) in the delivery settings. Message type is what [bypasses user opt-outs and the control group](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). Frequency capping alone does not. 7. Complete the Journey configuration and click **Launch campaign.** ![Completed subscription renewal email journey ready for launch](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-5.webp) Once set up, the renewal notifications will be sent automatically when the subscription expired event is triggered by the third-party service. ## Sending a password reset message To send a password reset email, you’ll need to set up the password reset event, configure the password reset link on your end, and then [build a Customer Journey](https://docs.pushwoosh.com/product/customer-journey/create-a-customer-journey-for-your-project) to send the email. Follow the steps below to complete the setup: ### Step 1. Configure the password reset event in Pushwoosh When a user requests a password change, you trigger an event, such as **password change requested**. You need to set up this event in advance. 1. Navigate to **Events** in the Pushwoosh dashboard and click **Create event**. 2. Name the event, for example, **Password reset request**. 3. Add the following attributes to the event: | Attribute Name | Type | Description | Example Value | | ----------------- | ------- | ------------------------------------------- | ---------------------------------------------- | | reset_link | String | The unique password reset link. | | | user_name | String | The user’s name for personalization. | John | | token_durability | Integer | Token validity in seconds (e.g., 24 hours). | 86400 | | date | Date | The date and time of the reset request. | 2024-11-18T10:00:00Z | These attributes act as placeholders, receiving dynamic data from your system when the event is triggered. Important In the attributes of the event, you must pass a unique password reset link that you generate on your side, or a unique key that needs to be included in the link for it to work. This link must contain a unique token that is personalized for each user and embedded in the URL through an event attribute sent from your system. The token should have an expiration period (e.g., 24 hours) to ensure security, allowing users to reset their password only within the specified timeframe. ![Event configuration interface showing password reset request event with required attributes](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-6.webp) Set up the event in your app. [Learn more about setting up custom events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#implementation) ### Step 2. Generate a password reset link When a user requests a password reset (e.g., by clicking “Forgot Password”), you’ll need to: 1. **Generate a unique token**. Create a secure, randomly generated token for the user. For example, 12345-abcde-67890. 2. Combine the token with your password reset URL. For example, . 3. Save the token in your database, linked to the user’s account, and set an expiration time (e.g., 24 hours). 4. Include the reset link in the reset_link event attribute when triggering the **Password Reset Request** event. ### Step 3. Create email content 1. Navigate to **Email content** in Pushwoosh and create new email content. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/#create-new-email-content) 2. Add placeholders for dynamic content: 1. `{reset_link}` for the password reset link. 2. `{user_name}` for the user’s name. 3. `{token_durability}` for the link expiration time. ![Email content editor with placeholders for reset_link, user_name, and token_durability fields](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-7.webp) ### Step 4. Create the Campaign 1. Navigate to the **Customer Journey** section in Pushwoosh and click **Create Campaign**. 2. Drag and drop a **Trigger-based entry** element onto the canvas. 3. Configure the trigger to activate when your system sends the **Password Reset Request** event. ![Customer Journey canvas showing Trigger-based entry element configured for Password Reset Request event](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-8.webp) 4. Add an **Email** element to the canvas. 5. Link the email to the **Trigger-based entry** element. 6. Select the previously created email content with placeholders for the reset link and user details. ![Email element linked to trigger entry with password reset email content and dynamic placeholders](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-9.webp) 7. In the [Email element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/#set-message-type), set **Message type** to **Transactional**, then also select **Send messages without global frequency capping** in the delivery settings. Message type is what [bypasses user opt-outs and the control group](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). Frequency capping alone does not, so a password reset left as Marketing can silently skip an unsubscribed user. 8. After adding the message content, ensure all steps in the journey are configured correctly. End the branch with the **Exit** element or by leaving it without an outgoing connection, then click **Launch campaign** to start the journey. ![Completed password reset email journey with Exit element configured ready to launch](https://docs.pushwoosh.com/how-to-create-campaigns-how-to-send-transactional-messages-10.webp) Once activated, the **password reset message** will automatically be sent when a user requests to reset their password, providing a seamless and secure experience. --- # Personalization **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/ Send personalized push notifications, in-app messages, emails, and SMS using Dynamic Content and user attributes in Pushwoosh. --- In Pushwoosh, you can send highly personalized messages to your users through different channels: mobile and web push notifications, emails, in-app messages, and SMS. To create a personalized message, you can use personalization tags for user attributes, known as [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content). For example, the tag ‘first name’ will place a user attribute [Laura] into your message: Hi, [Laura]. All the personalization tags available for your messages are created and stored in the **Audience > Tags** section. [Follow the guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) to create new personalization tags. If you want to use personalization via Liquid syntax, [follow this guide](https://docs.pushwoosh.com/product/personalization/liquid-templates). Tip You can add as many personalization tags as you want. ## Personalization guides by channel Ready to implement personalization across your messaging channels? These step-by-step guides will help you set up and send personalized messages for each channel: [Send a personalized push ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-push-notification) [Send a personalized in-app ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message) [Send a personalized email ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-email) [Send a personalized SMS ](https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-sms) --- # How to send a personalized email **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-email/ Deeply personalized emails in Pushwoosh --- In Pushwoosh, you can personalize the email subject and email body accordingly. Use personalization tags for user attributes, known as [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content), to personalize your email. You can use personalization tags when creating emails in the [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) and customizing one of the pre-built email templates. Tip You can also import emails designed in HTML. In this case, you can use [Liquid syntax](https://docs.pushwoosh.com/product/personalization/liquid-templates) in the HTML layout. All the personalization tags available for your emails are created and stored in the **Audience > Tags** section. [Follow the guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) to create new personalization tags. Tip You can add as many personalization tags as you want. ## How to personalize an email subject Go to **Content > Email Content > Create email content** to start personalizing your email. ![Email Content creation interface showing Content menu, Email Content section, and Create email content button](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-email-1.webp) Open an email you want to personalize. In our example, we are using one of the email templates. The **Subject line** and **Preheader** fields don’t have a tag picker, only **Insert emoji** and **Generate with AI** next to them. To personalize either one, type [Liquid syntax](https://docs.pushwoosh.com/product/personalization/liquid-templates) directly into the field, for example `{{first_name}}`. ![Subject line and Preheader fields, each with only an Insert emoji icon and a Generate with AI icon, no personalization tag picker](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-email-2.webp) ## How to personalize an email body In the email template, click on the text where you want to add personalization. Click **Merge Tags** and choose the tag to personalize. Click **Save** to apply changes. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-email-3.webm "Email template body with text selected and Merge Tags option displayed to add personalization tags") [Read more about how to create emails in Pushwoosh.](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor/create-email-content-with-drag-and-drop-editor/) --- # How to send a personalized in-app message **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-in-app-message/ Learn how to create personalized in-app messages in Pushwoosh using user data tags and dynamic content. --- In Pushwoosh, you can send highly personalized in-app messages. To create a personalized in-app message, use personalization tags for user attributes, known as [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content). For example, the tag ‘first name’ will place a user attribute [Laura] into your in-app message: ![In-app message example showing personalization with first name tag displaying user attribute Laura in the message](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-in-app-message-1.webp) All the personalization tags available for your in-app messages are created and stored in the **Audience > Tags** section. [Follow the guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) to create new personalization tags. Tip You can add as many personalization tags as you want. In your Pushwoosh account, open **Content > In-Apps > Create in-app**. ![Navigation menu showing Content section with In-Apps Rich Media option and Create in-app button highlighted](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-in-app-message-2.webp) You can add a personalization tag when creating an in-app banner in [the no-code editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/) or customizing our pre-built in-app templates. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-in-app-message-3.webm "In-app editor showing no-code editor interface with pre-built templates available for customization") > **Note**: You can also import your pre-designed in-apps via ZIP import. In this case, you can use [Liquid syntax](https://docs.pushwoosh.com/product/personalization/liquid-templates) in the HTML layout. This guide covers the custom, HTML-based in-app editor. For SDK-rendered [native in-apps](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/), personalization works the same way but lives directly in each text field — see [Add dynamic content](https://docs.pushwoosh.com/product/content/in-apps/create-native-in-app-messages/#add-dynamic-content). ## In-app personalization example Let’s add personalization to an in-app message by customizing one of our in-app templates. Choose any pre-built in-app template from the library. You can add new blocks to the in-app or edit its copy, images, and buttons. To add a personalization tag, click on the place in the block where you want personalization. In the pop-up menu, click **Merge Tags** and choose a tag to be personalized. You can personalize the in-app copy and button text. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-in-app-message-4.webm "In-app message editor with text block selected showing Merge Tags option to add personalization tags") Click **Save** to apply changes. [Read more on how to create in-app messages.](https://docs.pushwoosh.com/product/content/in-apps) --- # How to send a personalized push notification **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-push-notification/ Learn how to send personalized mobile and web push notifications in Pushwoosh using dynamic content and user attributes. --- In Pushwoosh, you can send highly personalized mobile and web push notifications. To create a personalized message, you can use personalization tags for user attributes, known as [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content). For example, the tag ‘first name’ will place a user attribute [Laura] into your push notification: Hi, [Laura]. All the personalization tags available for your messages are created and stored in the **Audience > Tags** section. [Follow the guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) to create new personalization tags. If you want to use personalization via Liquid syntax, [follow this guide](https://docs.pushwoosh.com/product/personalization/liquid-templates). Tip You can add as many personalization tags as you want. In your Pushwoosh account, open **Content > Push presets > Create content**. ![Navigation showing Content menu with Push presets section and Create content button](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-push-notification-1.webp) ## How to personalize a push title Click on the Title field and then click the personalization icon on the right. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-push-notification-2.webm "Title field in push notification editor with personalization icon highlighted on the right") **Note**: Title is an optional field. Type your text and click on the part of it where you want personalization. Choose the tag(s) you want to use—for example, [first_name]. Click **Insert** to apply the changes. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-push-notification-3.webm "Push notification text editor showing Merge Tags dropdown with first_name tag selected") ## How to personalize an iOS push subtitle An iOS subtitle is displayed in bold print under the title; you can personalize it like other text elements of a push notification. Yet, the Subtitle is an optional field. **Note**: Your iOS push subtitle will not be displayed in Android push and Windows web push notifications, but it will be displayed in macOS web pushes. Click on the Subtitle field and click the personalization icon on the right. Choose the tag you want to use—for example, [first_name]. Click **Insert** to apply changes. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-push-notification-4.webm "iOS subtitle field in push editor with personalization icon and tag selection dropdown menu") ## How to personalize a push message The push message is the only required field. It will be displayed on all platforms. To personalize your push message, click on the Push message field and then click the personalization icon on the right. Choose the tag you want to use—for example, [first_name]. Click **Insert** to apply changes. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-push-notification-5.webm "Push message field editor with personalization icon and tag selection options displayed") After personalizing your message, name your push preset and click **Save**. [Read the guide on how to create push presets](https://docs.pushwoosh.com/product/content/push-presets). --- # How to send a personalized SMS **URL:** https://docs.pushwoosh.com/tutorials/product-guides/how-to-send-a-personalized-message/how-to-send-a-personalized-sms/ Deeply personalized SMS with Pushwoosh --- In Pushwoosh, you can personalize the SMS you send to customers. To do so, use personalization tags for user attributes, known as [Dynamic Content](https://docs.pushwoosh.com/product/personalization/dynamic-content). Tip You can add as many personalization tags as you want. All the personalization tags available for your SMS are created and stored in the **Audience > Tags** section. [Follow the guide](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags) to create new personalization tags. To personalize your SMS copy, go to **Content > SMS Presets**, click **Create SMS content**, and choose **Regular SMS**. ![Content menu with SMS Presets section and Create content button](https://docs.pushwoosh.com/shared-48.webp) To personalize the SMS, click on the Message field and then click the personalization icon on the right. Choose the tag you want to use, for example, [first_name]. Click **Insert** to apply changes. [](https://docs.pushwoosh.com/how-to-send-a-personalized-message-how-to-send-a-personalized-sms-1.webm "SMS message field editor with personalization icon highlighted and first_name tag selection menu") [Read more about how to create and send SMS in Pushwoosh](https://docs.pushwoosh.com/product/content/sms-and-messenger-presets/sms-presets/#create-an-sms-preset). --- # User lifecycle **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/ Set up onboarding, activation, engagement, retention, re-engagement, and anniversary campaigns in Pushwoosh Journey Builder. --- Lifecycle campaigns in Pushwoosh are Customer Journeys tied to where the user is in your product. You can run these across push, email, in-app, and other channels on one journey canvas. [Onboarding ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh)First sessions after install or registration. [Activation ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh)First key actions that show the product’s value. [Engagement ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh)Ongoing prompts while the user is already active. [Retention ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh)Keep people who might drift away. [Re-engagement ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh)Win back users who went quiet. [Anniversaries ](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-anniversary-campaigns)Birthdays, signup dates, and other fixed calendar moments. --- # How to create a re-engagement campaign in Pushwoosh **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-a-re-engagement-campaign-in-pushwoosh/ Learn how to create a re-engagement campaign in Pushwoosh to win back inactive users and boost app engagement. --- In the highly competitive world of mobile apps, [engaging users](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh/) is only half the battle. Some users may open the app only a few times and then forget about it. Launching a re-engagement campaign will encourage these users to return to the app. Tip For a step-by-step guide on creating a re-engagement campaign, watch this video or refer to the guide below for more detailed instructions. [Youtube Video: Step-by-step guide on creating a re-engagement campaign](https://youtube.com/watch?v=w3VIT1CJM_E) ## Segment your users First, identify the categories of users at risk of churning. Most often, these are the users who have not opened your app for three to seven days. When starting a campaign in Pushwoosh, you can select the automatically created segments called *Didn’t open the app for 3 days* and *Didn’t open the app for 7 days* as the audience source. You can also segment users based on the preferences they shared in their profiles or during [onboarding](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/). Then, based on the preferences, you can run dedicated campaigns using personalized content and relevant communications. ## Prepare content Use different types of content and various channels to make your campaign more engaging and effective. The following communication channels are most suitable for creating a re-engagement campaign in [Pushwoosh Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview). * **Push notifications** are a powerful tool for reminding users about your app and establishing a habit of using it. * **In-app messages** allow you to demonstrate the app’s key features and benefits, provide helpful tips and ask for feedback. * **Emails** are perfect for offering exclusive discounts, showing new features, or presenting engaging content. For each of these channels, Pushwoosh allows you to [personalize communications](https://docs.pushwoosh.com/product/personalization/dynamic-content/#how-to-personalize-the-push). For example, you can insert the user’s name or mention their preferences in push notifications, in-app messages, and emails. ## Create a Customer Journey Once you’ve defined the audience and prepared content for your campaign, it’s time to create a Customer Journey. To do this, navigate to **Campaigns → Customer Journey Builder → Create Campaign**. ![Pushwoosh Customer Journey Builder canvas showing campaign creation interface and available elements](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-1.webp) Re-engagement campaigns can be very different depending on the type of application, user behavior patterns, and many other factors. As an example, we will create a re-engagement campaign for a music streaming app: ![Re-engagement campaign structure example for music streaming app showing multiple journey branches](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-2.webp) We have already created an [example engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh#create-a-customer-journey) for this app and will use some of the data obtained at that stage in our new journey. ### 1. Entry point We want to launch a targeted re-engagement campaign for those who have previously listened to our Modern jazz playlist. Therefore, we will add an Audience-based entry point and select the *Listened to Modern jazz playlist* Segment as the audience source: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-3.webm "Audience-based entry point configuration with Listened to Modern jazz playlist segment selection") Note First, we created the *Listened to Modern jazz playlist* [Segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) using the *Listened to Modern jazz playlist* [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/). This Tag was assigned to users during our [example engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh#create-a-customer-journey). You can also use predefined Segments as the audience source. For example, the *Didn’t open the app for 3 days / 7 days* Segments are suitable for a re-engagement campaign. ### 2. Waiting for users to open the app We will wait seven days for the Entry point audience to open the app. To do this, we will add the **Wait for Trigger** element and select the predefined *PW_ApplicationOpen* Event with a seven-day waiting period: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-4.webm "Wait for Trigger element configuration showing PW_ApplicationOpen event and seven-day waiting period") Note You do not need to set this trigger if you selected the *Didn’t open the app for 3 days / 7 days* Segments as the audience source. If a user opens the app within seven days, they will exit the Journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-5.webm "Journey branch showing app open trigger with exit path for users who opened app within seven days") ### 3. Push notification Let’s send a push notification to those who have not opened the app for seven days. We will offer them to listen to the top jazz tracks they haven’t heard yet: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-6.webm "Push notification element configured with top jazz tracks offer for inactive users") Note To make this push notification, we collected the best jazz tracks that were not included in the playlist used in our [engagement campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh#create-a-customer-journey). After that, we added a [Deep Link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) leading to the playlist in our example app. Finally, we configured a push message containing this Deep Link and [saved it as a Preset](https://docs.pushwoosh.com/product/content/push-presets). We will also configure [Silent Hours](https://docs.pushwoosh.com/product/customer-journey/journey-settings/#silent-hours) so that push notifications are not sent to users at inappropriate times of the day: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-7.webm "Silent Hours configuration preventing push notifications during inappropriate times of day") ### 4. Listening time trigger and profile update *for those who opened the push notification* Now we want to split the users who opened the push into two groups: those who listened to the playlist for a specific amount of time and those who closed the playlist quickly. First, we need to configure the *N minutes listened* [Event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) in the app and the Pushwoosh project. Note To configure the event, you’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. Now, let’s add the **Wait for Trigger** element and specify the *N minutes listened* Event: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-8.webm "Wait for Trigger element with N minutes listened event for tracking user engagement with playlist") If a user has listened to the playlist for a sufficient time, we tag them as *Listened to the new jazz playlist*. To do this, we will use the **Update User Profile** element: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-9.webm "Update User Profile element tagging users as Listened to the new jazz playlist") Note Before configuring this step, we created the *Listened to the new jazz playlist* [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) with the *boolean* data type. Tags can help you segment the audience further and send relevant recommendations in the future. If a user does not trigger the *N minutes listened* Event, they exit the journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-10.webm "Journey exit path for users who did not trigger the N minutes listened event") ### 5. Waiting for users to open the app *for those who did not open the push notification* If a user ignores our push notification, we will wait for them to open the app for three days: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-11.webm "Wait for Trigger element configured with three-day wait period for users ignoring push notification") ### 6. In-app message *for those who opened the app within three days* If a user has opened the app within three days, we will show them the **In-App** offering to collect feedback on our app. This will help us understand why users abandon the app. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-12.webm "In-app message element showing feedback form offering to understand user abandonment reasons") Note We created an [in-app message](https://docs.pushwoosh.com/product/content/in-apps) with a button leading to the feedback form and selected this in-app message in the **In-App** element. After showing the in-app, end this branch here — with an **Exit** element, or by leaving it without an outgoing connection: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-13.webm "Adding the Exit element after users open the app and view the in-app message") ### 7. Email *for those who have not opened the app within three days* Let’s email users who have not opened our app within three days. The email will contain brief information about the best jazz musicians of the 21st century and links to playlists with tracks performed by them. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-14.webm "Email element configured with jazz musicians information and playlist links for inactive users") Note To send the email, ask your development team to [configure the Email platform](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification). Then create the email using the Pushwoosh [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) or [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor). After sending the email, end this branch the same way: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-a-re-engagement-campaign-in-pushwoosh-15.webm "Adding the Exit element after the re-engagement email is sent") ## You may also find interesting * [More about Pushwoosh Customer Journey Builder](https://www.pushwoosh.com/products/customer-journey/) * [How to increase app user engagement](https://blog.pushwoosh.com/blog/increase-app-engagement/) --- # How to create anniversary campaigns **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-create-anniversary-campaigns/ Learn how to create automated birthday and anniversary campaigns with Pushwoosh to boost user engagement. --- Pushwoosh enables you to create segments based on the anniversary of a specific date tag. This allows you to target users whose significant dates, like birthdays or first installs, occur on the same day each year. With this feature, you can run personalized automated campaigns to celebrate these anniversaries, such as sending birthday greetings or exclusive offers to loyal customers. ## Create an anniversary segment For this, do the following: 1. Navigate to the Segments section and click **Create Segment** to open the segment builder 2. Select the tag relevant to your anniversary (e.g., *First Install* or user’s birthday). ***First Install** is a default tag. If you need a custom tag like “birthday”, create one before creating the segment. [Learn more](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags)* 3. Choose the condition **Anniversary** and specify the date condition: * **Today**: Users whose anniversary date matches today’s date. * **Is in N days**: Users whose anniversary is in N days. * **Is N days past**: Users whose anniversary is N days past. ![Anniversary segment configuration showing condition options for Today, Is in N days, and Is N days past](https://docs.pushwoosh.com/shared-18.webp) Configuring a segment to include users a certain number of days before or after the anniversary date allows for pre- or post-anniversary engagements. 4. Refine your segment further by adding more filters. ## Set up a daily campaign with the segment Once you’ve created a segment, set up a recurring customer journey based on this segment: 1. Navigate to **Customer Journey Builder** in your Pushwoosh account and click **Create campaign**. 2. Drag the **Audience-based entry element** to the Journey canvas and select the segment you created earlier. 3. In the Audience-based entry element, set **Entry schedule** → **Recurring entry** → **Periodically** (e.g. every day). [Learn more](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/#periodically) ## Example scenario: Pre-birthday promo Imagine you want to send users a push notification with a birthday promo code three days before their birthday. Here’s how to set it up: 1. Create a **Birthday tag** with a date tag in the **Tags** section of your Control Panel. This will store users’ birthdays. 2. Then, navigate to the Segments section and click **Create Segment.** 3. Click on the **Tag** icon and select the Birthday tag you’ve created. ![Birthday tag selection in Segments section showing custom birthday tag creation](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-anniversary-campaigns-1.webp) 4. Next, select the condition **anniversary** and set the date condition to **Is in N days - 3 days** to target users whose birthday is in three days. ![Anniversary segment condition set to Is in N days with 3 days specified for pre-birthday targeting](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-anniversary-campaigns-2.webp) 5. In **Customer Journey Builder**, drag the Audience-based entry element to the canvas and choose the segment you’ve created. 6. Set **Entry schedule** → **Recurring entry** → **Periodically** and configure the journey to run every day (e.g. 11:00 in the subscriber’s timezone). ![Audience-based entry with Entry schedule set to Recurring entry, Periodically, run every day](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-anniversary-campaigns-3.webp) 7. Add a **Push** element to the canvas and craft a personalized message with a pre-birthday promo code, such as: “*Something special is coming… Celebrate with us early and use code BDAY2024 for exclusive savings!”* ![Push element on canvas with personalized pre-birthday promo message and BDAY2024 code](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-create-anniversary-campaigns-4.webp) 8. Click **Launch campaign** to activate the journey. --- # How to onboard users with Pushwoosh **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/ Learn how to create seamless onboarding campaigns with Pushwoosh Customer Journey Builder to boost user engagement and retention. --- As the first personal touch, user onboarding must be perfectly tailored, seamless, and gently engaging. Run your onboarding campaigns with Pushwoosh smoothly and easily. Customer Journey Builder lets you create mobile and email onboarding campaigns with a simple visual editor. ## Mobile onboarding campaign Prerequisites * First, adjust your onboarding flow based on your app’s value and what you want to engage users with at these first steps. * Prepare content for your onboarding campaign. Set up in-app content and push notification presets. Unleash your creativity: you can customize the pre-built [in-app templates](https://docs.pushwoosh.com/product/content/in-apps/), build your own in-app pages using our [no-code editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/), try out the [AI composer](https://blog.pushwoosh.com/blog/ai-composer/) for pushes, or create your tailored content from scratch! * Implement Pushwoosh [Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) to keep track of newcomers and their first steps in your app. ![Example of an onboarding campaign](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-1.webp) Here are a few steps to onboard users who recently joined your mobile app with Pushwoosh Customer Journey Builder. ### Trigger-based Entry Define the event the journey starts with: it might be the first-time app launch, registration in the app, or first in-app actions. 1. In this example, we’ll use the default [PW_DeviceRegistered](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/default-events/#pw_deviceregistered) event, which is triggered when the app is launched for the first time. To enable this event, open your Pushwoosh Control Panel, go to **Audience → Events**, and click **Create event**. In the dropdown, select **Default events**, then find **Device Registered** in the list and click **Activate**. ![Device registration event (app launch)](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-2.webp) Note If you want to configure your custom Event, [create it](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) in the Control Panel and integrate the corresponding [postEvent](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) into your app. You’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. 2. Add the [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) step to the journey canvas and select the required Event. ![Setting the journey entry](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-3.webp) ### Welcome In-App **Welcome users warmly:** Add the welcoming [In-App message](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) to be shown right after the trigger event. Tip For a step-by-step video guide on sending a welcome in-app message, watch this tutorial [Youtube Video: Sending a welcome in-app message](https://youtube.com/watch?v=DGEw6ANfGc0) 1. Create a Rich Media greeting the user and describing what they will experience with the app. You can use the Welcome Newcomers template or create a new one fitting your app’s look and feel. 2. Add the [In-App step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/) following the journey’s start event and select the pre-created welcome Rich Media. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-4.webm "Welcome In-App") ### Acquaintance In-App On the next journey step, let your users tell a bit about themselves: ask users in the journey a few questions to personalize their further experience. 1. [Create a Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#how-to-set-up-a-custom-tag) of the list type to gather users’ preferences. ![Creating a list tag](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-5.webp) 2. Create an [in-app message template](https://docs.pushwoosh.com/product/content/in-apps) containing the list of options a user can choose. You can upload an HTML file with a ready-made template or build an in-app page from scratch using our [block-based editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor). ![Creating Rich Media](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-6.webp) 3. [Set the Tag values](https://docs.pushwoosh.com/developer/api-reference/device-api#settags) based on what options users select. 4. Follow the first welcome screen with the [In-App element ](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/in-app/)containing the Rich Media you’ve created. ![First onboarding steps](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-7.webp) ### Opt-in prompt Help users get the most out of your app with push notifications – show the in-app subscription prompt to encourage users to sign up for your messages. 1. Create an in-app opt-in prompt. You can use the ready-made template, upload your HTML, or create a new template using [the built-in editor](https://docs.pushwoosh.com/product/content/in-apps/no-code-in-app-editor/create-rich-media-in-the-built-in-editor/). ![Opt-in Rich Media template](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-8.webp) 2. Add the In-App step to your journey to display this Rich Media. Once a user subscribes to push notifications, the Pushwoosh SDK sets the [default Push Alerts Enabled tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/#default-tags) for this user to ‘true’. ![Opt-in prompt on the canvas of Pushwoosh Customer Journey Builder](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-9.webp) ### Time delay Set a [delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/) for a couple of hours to give users some time to explore your app and sign up for pushes. ### Segment Splitter Split users in the journey into branches based on what they have decided: for those who enabled push notifications, it is time to get their first one! For those who have not, one more In-App can help them understand the value of your push notifications. 1. [Create a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of users subscribed to push notifications using the Push Alerts Enabled tag. ![Creating a segment](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-10.webp) 2. Add a [Condition split element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split) to the canvas. Select the segment you’ve created. 3. Follow the segment branches with corresponding communication flows: * Send a [first push notification](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) encouraging opted-in users to open the app and continue exploring its features; * Show others one more In-App on their next app launch. ![Completed mobile app onboarding journey with welcome, preferences, opt-in, and push notification branches](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-onboard-users-with-pushwoosh-11.webp) ### Exit Do not overwhelm users with too many onboarding steps – let them enjoy your app after the essential onboarding is completed and start your engagement campaign! ## Email onboarding campaign To onboard users via email messages, the basic flow is quite the same: * Consider the onboarding steps the newcomers should take first. * Prepare engaging onboarding email content. [Learn more](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) * Make sure to integrate all the necessary [Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/), for example, signing up for emails or leaving the email address in a registration form. * Create [audience segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) needed for your onboarding campaign splitters based on your onboarding goals and communication flow. * Finally, build your email onboarding campaign in [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/journey-elements)! --- # How to set up a retention campaign in Pushwoosh **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/ Learn how to create effective retention campaigns in Pushwoosh Customer Journey Builder to keep users engaged and reduce churn. --- Tip Watch a video on how to increase mobile app retention with behavioral communication, or refer to the guide below for detailed instructions. [Youtube Video: How to increase mobile app retention with behavioral communication](https://youtube.com/watch?v=_KAhfFyhELg) If you have already successfully [onboarded](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) and [activated](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/) a user, this may still not be enough for them to continue using your app for a long time. Create retention campaigns in Pushwoosh [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) to keep users returning to your app and reduce churn rate. Tips for creating an effective retention campaign: * **Provide value:** show how your app will entertain your users or help them solve their problems. * **Personalize communication:** send content that matches the user’s preferences, address users by their names, and use a friendly tone. * **Use multiple channels:** send push notifications, show in-app messages or launch retention email campaigns depending on the type of your business and the stage your user is currently on. * **Continuously improve your campaigns:** experiment with segmentation and sending time, analyze user behavior, reduce drop-offs, and make more relevant content and offers. Below, we will show you how to create two types of retention campaigns in Customer Journey Builder: a [user retention campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/#user-retention-campaign) and a [customer retention campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-retention-campaign-in-pushwoosh/#customer-retention-campaign). In our examples, these two types of campaigns have different conversion goals: the user retention campaign appeals to taking the target action in the app, while the customer retention campaign motivates buyers to make another purchase. As the goals differ, the customer journey is also different for these two types of campaigns. ## User retention campaign Let’s draft a retention campaign for a paid-subscription workout app. Once complete, it will look like this: ![Workout retention campaign flow showing entry point, triggers, push notifications, and conversion goal](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-1.webp) **1. Conversion goal** Setting a conversion goal is necessary so that users do not re-enter the same Journey if they have already completed the goal. We will assume that our user retention campaign reaches its goal if a user completes the workout. First, we will create the *Workout complete* [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) in the Pushwoosh project and configure it in our example workout app using the [postEvent](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#3-call-postevent-api) API. Note To set up the event, you’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. After that, we will start creating a new Customer Journey. On the top bar, we will click on **Conversion Goals** and select the *Workout complete* event. We want the Journey to end for the user that fires the target event, so we will enable this option and then apply changes: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-2.webm "Conversion goal configuration with Workout complete event selected in Customer Journey Builder") **2. Entry point and trigger** We want to launch a retention campaign for app subscribers who haven’t opened the app for seven days. First, we will add the **Audience-based Entry** and select *Subscribers* as the audience source: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-3.webm "Audience-based entry element showing Subscribers segment selection for retention campaign") Note We assigned a particular [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) to each user once they’ve subscribed to the app. Using this Tag, we compiled the *Subscribers* [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/by-tags/). Then, we will add the **Wait for Trigger** element and specify the default *PW_ApplicationOpen* event with a seven-day waiting period: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-4.webm "Wait for Trigger element configured with PW_ApplicationOpen event and seven-day waiting period") Note The waiting period may vary depending on the category of your app. If a user opens the app at least once within seven days, they will exit the Journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-5.webm "Journey exit path for users who opened app within seven-day waiting period") Note You can also build the desired segment automatically using the [RF(M) segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/) feature. **3. First push notification** If a user has not opened the app at least once in seven days, we will send them a push notification offering to try an exclusive workout: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-6.webm "First push notification element with exclusive workout offer for inactive users") Note To make this push notification, we first created a [Deep Link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) leading to an exclusive workout in our example app. Then, we configured a push message containing this deep link and [saved it as a Preset](https://docs.pushwoosh.com/product/content/push-presets#_top). To get the best results, you can [personalize push notifications](https://docs.pushwoosh.com/product/personalization/dynamic-content#how-to-personalize-the-push): for example, insert the user’s name or optimize content based on the workout preferences they shared in their profile or during onboarding. Now, let’s split the flow depending on whether this [push is opened or ignored](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/#split-flow-by-opens-or-ignores): [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-7.webm "Push notification split showing opened and ignored branches for follow-up actions") We will also set [Silent Hours](https://docs.pushwoosh.com/product/customer-journey/journey-settings#silent-hours) to prevent users from receiving workout push notifications at inappropriate times: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-8.webm "Silent Hours configuration preventing push notifications during inappropriate times") **4. Workout trigger** *for those who opened the first push notification* If a user clicks on the push notification, we will check if they have completed the workout. First, we will set up the *Workout complete* [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) in the app and the Pushwoosh project. Then, we will add the **Wait for Trigger** element with the *Workout complete* event selected: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-9.webm "Wait for Trigger element with Workout complete event monitoring user engagement") If a user completes the workout, they will exit the Journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-10.webm "Journey exit path for users who completed workout conversion goal") **5. Time delay** *for those who did not open the first push notification or did not complete the workout* If a user ignores the push notification or does not complete the workout, we will wait for two days before sending anything else. To do this, we will add the **Time Delay** element: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-11.webm "Time Delay element configured for two-day pause before second push notification") **6. Second push notification** After the specified time, we will send the second push notification prompting users to take a survey and tell about their preferences. This survey will help us improve our workout app and launch personalized campaigns in the future. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-12.webm "Second push notification prompting users to take survey about preferences") After sending the second push notification, end this branch — with an **Exit** element, or by leaving it without an outgoing connection: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-13.webm "Adding the Exit element to end the retention campaign journey") ## Customer retention campaign As an example of a customer retention campaign, we will create a Customer Journey for a clothing app. Once complete, it will look like this: ![Customer retention campaign flow for clothing app showing purchase conversion goals](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-14.webp) **1. Conversion goal** Setting a conversion goal is necessary so that users do not re-enter the same Journey if they have already completed the goal. We will assume that our customer retention campaign reaches its goal if a user makes a purchase. On the top bar of the new Customer Journey window, we will click on **Conversion Goals** and select the default *PW_InAppPurchase* event. We want the Journey to end for the user after the event fires, so we will enable this option and then apply changes: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-15.webm "Conversion goal configuration with PW_InAppPurchase event for customer retention") **2. Entry point and trigger** We are going to launch a retention campaign for customers who purchased something recently. We want to motivate these people to buy more often and improve their loyalty to our e-commerce app. First, we will calculate segments of users who made a purchase in our app at least once. To do this, we will go to the [RFM segmentation](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/rfm-segmentation/) section and select the default *PW_InAppPurchase* event: ![RFM segmentation section showing default PW_InAppPurchase event customer segments](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-16.webp) Pushwoosh will automatically create several customer segments of users who performed the *PW_InAppPurchase event*. We will use the *Potential loyalists* segment because it includes customers who purchased something recently. Note You can also [build Segments manually](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/create-segments/) if you need narrow segments with specific parameters. Now we will add the **Audience-based Entry** and select *RFM Potential loyalists* as the audience source: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-17.webm "Audience-based entry element with RFM Potential loyalists segment selection") **3. Push notification** We will send our *Potential loyalists* a push notification about the sale in our example app: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-18.webm "Push notification about sale sent to potential loyalist customers") Note To make this push notification, we first created a [Deep Link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) leading to the sale section in our clothing app. Then, we configured a push message containing the deep link and [saved it as a Preset](https://docs.pushwoosh.com/product/content/push-presets#_top). Let’s split the flow depending on whether the customer opened the push: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-19.webm "Push notification split branch showing opened and ignored paths for customers") It is also important to set **Silent Hours** so that users do not receive push notifications at odd hours: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-20.webm "Silent Hours configuration for customer retention campaign notifications") **4. New purchase trigger** *for those who opened the push notification* If a user is interested in our sale and clicks on the push notification, we will check if they make a new purchase within two days. To do so, we will add the **Wait for Trigger** element and specify the default *PW_InAppPurchase* event with a two-day waiting period: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-21.webm "Wait for Trigger element with PW_InAppPurchase event and two-day waiting period") If a user makes a new purchase, they will exit the Journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-22.webm "Journey exit path for customers who made purchase within waiting period") **5. Tagging users who have not made a purchase** *for those who opened the push notification but has not purchased anything* If a user has opened the push notification but has not purchased anything within two days, we will update their profile to assign them the *Retention drop-off* Tag with the *Didn’t like the sale* value. This will allow us to make a segment of customers who were interested in the sale but did not find anything suitable. Later, we can think about what might interest such customers more and launch another retention campaign for them. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-23.webm "Update User Profile element assigning Retention drop-off tag for uninterested customers") Note We created the *Retention drop-off* [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) beforehand and selected the string data type. **6. Time delay** *for those who did not open the push notification* If a user ignores the push notification, we will wait for three days before sending anything else. To do this, we will add the **Time Delay** element: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-24.webm "Time Delay element set for three days before sending follow-up email") **7. Email** After the specified time, we will send an email with a selection of discounted clothes. First, ask your development team to [configure the Email platform](https://docs.pushwoosh.com/product/first-steps/start-with-your-project/configure-platforms/email-verification). Then, create the email using the Pushwoosh [Drag & drop editor](https://docs.pushwoosh.com/product/content/email-content/drag-and-drop-email-editor) or the [HTML code editor](https://docs.pushwoosh.com/product/content/email-content/html-code-editor). Next, we will add the **Email** element to our Journey and select the template we created. We will split the flow and wait for the email to be opened for three days: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-25.webm "Email element with discount clothing selection and open tracking split") Note Personalization is essential for e-commerce. If you [segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) customers based on their preferences and previous purchases, you can send relevant recommendations and offers for each segment. To send different messages depending on the segment, use the **Condition split** element in Journey. If a customer opens the email, we will wait for a new purchase within two days, and then the Journey will continue as described in steps 4 and 5: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-26.webm "Email opened branch waiting for purchase trigger within two days") **8. Tagging users who have not opened the email** If a user has not opened the email within three days, we will update their profile to assign them the *Retention drop-off* Tag with the *Not interested in offers* value. Using this Tag, we will be able to make a segment of customers who are not interested in any offer within our retention campaign or are not ready to buy clothes at the moment. We can launch another retention campaign for such customers in the future. [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-retention-campaign-in-pushwoosh-27.webm "Update User Profile element tagging unengaged customers as not interested") Note We created the *Retention drop-off* [Tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) beforehand and selected the string data type. ## You may also find interesting * [More about Pushwoosh Customer Journey Builder](https://pushwoosh.com/products/customer-journey) * [The best strategy to increase your mobile app retention](https://blog.pushwoosh.com/blog/increase-user-retention-rate/) --- # How to set up a user activation campaign with Pushwoosh **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/ Learn how to set up user activation campaigns in Pushwoosh Customer Journey Builder to convert newly-acquired users into engaged customers. --- User acquiring is tricky, [onboarding](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) is crucial, and user activation is a turning point – you can only say you’ve won a user when they are activated. User activation campaigns are intended to encourage customers to start using your product, i.e. to perform actions indicative of their engagement. [Pushwoosh Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview) is an ‘all-inclusive’ for building user activation campaigns – make your user flow smooth and fluent, and your customers won’t be long to benefit from your app! Given your newly gained users have already completed their onboarding, it’s time to move them forward to their first in-app action. Let’s say we’re building an activation campaign for a paid-subscription daily workout app. ![User activation campaign example](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-1.webp) Here are a few steps to launch an activation campaign ending with purchasing a subscription: 1. Define your main **activation goal**: what actions do you want users to perform? When do you count them as activated? 2. Decide **where to start** the journey: set up a trigger or a segment to begin the campaign. 3. Prepare **compelling content** – your activation campaign should convince users they really need your app. Leverage the omnichannel messaging for more conversions: create [Push Presets](https://docs.pushwoosh.com/product/content/push-presets) for short daily communication, [Email content](https://docs.pushwoosh.com/product/content/email-content) for delivering loads of value, and [in-app messages](https://docs.pushwoosh.com/product/content/in-apps/) to catch users’ eyes and hearts. 4. Do not forget to **measure your results**: collect user data with [Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/), keep track of your [journey statistics](https://docs.pushwoosh.com/product/statistics-and-analytics/journey-statistics), or run [A/B tests](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/abn-split/) to understand better what works best. ## Set up conversion goals As an activation campaign aims to encourage desired users’ behavior, its efficiency can be easily measured by the conversion rate. To track the journey’s conversions, set the [Conversion Goals](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals) – target actions you’d like users to perform. 1. First, [set the target event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#implementation) in your Pushwoosh Control Panel and [integrate it into your app](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#3-call-postevent-api) to be tracked. Note For this, you’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. 2. Then, add that event (or several events) as a [Conversion Goal](https://docs.pushwoosh.com/product/customer-journey/journey-settings#conversion-goals) of your Customer Journey. ![Setting Conversion Goals for the journey](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-2.webp) ## Add a trigger-based entry Before creating a journey, [set up the entry event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) in your Control Panel. Then, integrate the [postEvent](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events#3-call-postevent-api) to be sent upon the event triggering. Note For this, you’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. Finally, add this event as a [Trigger-based Entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) to the journey canvas. ![Trigger-based entry to journey](https://docs.pushwoosh.com/shared-6.webp) ## Add a time delay As users tend to interact with push notifications at a specific time of the day, it’s better to adjust your communications to users’ timezones and most convenient day times. For example, as a daily workout app, we’re helping users start their day healthily: before messaging users, wait till their morning by setting the timezone-sensitive [Time Delay](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/time-delay/). ![Time Delay journey point](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-3.webp) ## Send the first push notification The first communication of your activation campaign is essential: the more value it shows the user, the more likely the user will continue their journey. So, prepare the Push Preset in advance containing all the best you can offer at this journey point: for example, set the Deep Link directing users to a today’s workout, and friendly invite them to join. Note Save your message as a [Preset](https://docs.pushwoosh.com/product/content/push-presets#_top) to apply all its settings in your journeys. ![Creating a push preset](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-4.webp) Split the further flow depending on whether users open this push or not: for those who have not, one more reminder might work. ![Push notification settings](https://docs.pushwoosh.com/shared-54.webp) Note If you’ve learnt anything about users during the onboarding campaign, use that knowledge to [personalize your messages](https://docs.pushwoosh.com/product/personalization/dynamic-content/). ## Set up a wait for trigger For users who have opened the push message, let’s wait a bit to complete their first activation action. To track that, you have to set up a corresponding Event first, and add the [Wait for Trigger](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/wait-for-trigger/) step to your journey. Note Give users as much time as you consider sufficient – set the waiting period in days, hours, or even minutes, depending on your app’s flow. For this guide’s example, we’d say the first activation event is ‘Workout completed’, and it’s fair to wait a couple of days for the user to get involved. ![Wait for trigger](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-5.webp) ## Send the second push notification As for those users who have not opened your first push, add one more [Time Delay](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/#time-delay) for them and then send the second push notification offering a free workout. ![Second push notification](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-6.webp) ## Display an in-app message Since the Wait for trigger step splits the further flow into two branches – of users who trigger the event and those who do not – the former ones are ready for their next activation step. So, it’s time to make an offer: create an appealing [in-app message](https://docs.pushwoosh.com/product/content/in-apps/) offering a paid subscription and highlighting all its benefits for customers who’ve tried your product already. Note Make sure to [set users’ Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) accordingly. For example, if you offer users a subscription, create a ‘Paid subscription’ or ‘Subscription type’ tag for your app, and set its values upon a user completes their purchase. ![In-App with activation action prompt](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-7.webp) ## Add a preferences survey If a user has not performed the action you’d waited for, try to find out what might interest them. To do this, prompt them to take a survey about their workout preferences. This survey will help you improve your app and make more relevant and personalized offers. You can offer to take a survey to those who did not open the second push or did not complete the free workout. After that, you can end the journey for these users. ![Preferences survey](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-8.webp) ## Configure a condition split And that’s the whole turning point of your activation campaign – do users activate after all your efforts? To observe your results and build further user engagement relevantly, split users in the journey based on whether they’ve reached the goals you set. 1. First, [create a segment](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) of users who have performed an activation action: for example, those who purchased a subscription and thus have the ‘Paid subscription’ tag you’ve set on the previous step. ![Creating a segment](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-9.webp) 2. Then, add the [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/) element to your journey canvas. ![Setting a segment spliiter](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-10.webp) 3. Offer more to activated users, for example, give them a chance to personalize their experience with a push notification containing a Deep Link to their account settings or personal preferences choice. 4. If users are still not engaged enough to perform the activation event, you may want to get them a boost with a special, limited-time offer or a discount. ![Ending an activation campaign journey](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-11.webp) ## Launch the campaign That’s it! Your activation campaign is ready to start — branches without an outgoing connection are already a valid way to end it, so all you need is to check every step once again and launch the journey. ![Campaign is ready to start](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-activation-campaign-with-pushwoosh-12.webp) --- # How to set up a user engagement campaign in Pushwoosh **URL:** https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-engagement-campaign-in-pushwoosh/ Learn how to create effective user engagement campaigns with Pushwoosh Customer Journey Builder to increase retention and drive user loyalty. --- Has your new user already completed [onboarding](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) and [got activated](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-set-up-a-user-activation-campaign-with-pushwoosh/)? Great! Now it is time to ensure they maintain interest in your app. A well-designed engagement campaign will allow you to: * Build long-term relationships with users * Keep users loyal to your product * Increase revenue The steps below will help you set up an effective app campaign for engagement using our [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview). ## Think about campaign entry points In Customer Journey Builder, each campaign starts with an entry point that contains a specific trigger or user segment. Configure triggers or segments depending on what type of entry you want to use: * **Trigger-based entry:** the campaign is launched when a user triggers an event (i.e. performs a specific action). If you want to set a triggered-based entry, first [set up Events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) in your Pushwoosh project. Note To set up events, you’ll need help from your development team, so be sure to share [this link](https://docs.pushwoosh.com/developer/guides/audience-and-segmentation/events#custom-events-implementation) with them for detailed instructions. * **Audience-based entry:** the campaign is launched for all users of a specific segment. If you want to set an audience-based entry, first [set up Tags](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) that identify your users and [create Segments](https://docs.pushwoosh.com/product/audience-data-and-segmentation/segmentation/) based on these tags. To drive better engagement, you can create different campaigns depending on predefined triggers or user segments. You can also add several entry points to a single campaign: both trigger and audience-based. ## Prepare engaging content Think about what content you want to use for your engagement campaign. It may differ depending on user segments and behavior scenarios. You can also personalize the content (e.g. insert the user’s name or mention their preferences in a message). Personalized communications can improve CTR and conversion. In Customer Journey Builder, you can combine different channels for better engagement, so you may consider creating content specifically for these channels: * **Push notifications** are good for bringing users back into the app and establishing a habit of using it. It can be something interesting that urges users to open the app (e.g. alerts, news, special offers). * **In-app messages** are suitable for presenting helpful tips inside the app and asking for feedback. However, you cannot bring users back into the app by using in-apps only, so it’s better to use them along with push notifications. * **Emails** are perfect for industries like e-commerce. You can send follow-ups after some target action (e.g. purchase), give valuable tips and ideas on using your app, or make special offers. ## Create a Customer Journey Now it’s time to create your campaign in our [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/). ![Customer Journey Builder interface showing where to click to create a new campaign](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-1.webp) Campaign structure can vary greatly depending on your goals. In our example, we’ll show you how to set up a simple engagement campaign for a music streaming app: ![Music streaming app engagement campaign example showing jazz lovers segment flow](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-2.webp) ### Define entry points In our example music streaming app, we will add an audience-based entry point to engage jazz lovers: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-3.webm "Audience-based entry point configuration showing jazz lovers segment selection") Note To create a jazz lovers segment, we ran an [onboarding campaign](https://docs.pushwoosh.com/tutorials/product-guides/lifecycle-marketing-campaigns/how-to-onboard-users-with-pushwoosh/) to find out users’ music preferences. Each user was assigned a *Preferences* tag value according to their response. Using the values of this tag, we created segments based on musical tastes. ### Send an engaging push Let’s send a push notification to all jazz lovers about a special playlist in the app: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-4.webm "Push notification element showing special playlist offer to jazz lovers") Note To create this push notification, we first created a [deep link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) leading to a modern jazz playlist. Then we configured a push message containing this deep link and [saved it as a preset](https://docs.pushwoosh.com/product/content/push-presets#_top). We will split the flow depending on whether this push is opened or ignored: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-5.webm "Push notification split branch showing opened and ignored user paths") We will also set silent hours to avoid bothering users when they are not likely to be listening to music: ![Silent Hours configuration preventing notifications during inactive listening times](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-6.webp) ### Add a listening time trigger *for those who opened the push notification* Now we will divide those who clicked on the push notification into two groups: users who listened to the playlist for an extended period of time and users who took very little time before closing the playlist. First, we will set up the “N minutes listened” [event](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/) in the app and the Pushwoosh project. After that, we will add the **Wait for Trigger** element with the “N minutes listened” event selected: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-7.webm "Wait for Trigger element monitoring N minutes listened event for playlist engagement") ### Set a time delay *for those who did not open the push notification* If the user does not click on the push notification, we will set a one-day **Time Delay** before trying to get them back in the app again: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-8.webm "Time Delay element set for one day before re-engagement attempt") ### Update a user profile *for those who triggered the “N minutes listened” event* If the user has triggered the “N minutes listened” event, we will tag them as *Listened to Modern jazz playlist*. To do this, we will add the **Update User Profile** element: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-9.webm "Update User Profile element tagging engaged users as listened to jazz playlist") Note We created the *Listened to Modern Jazz playlist* [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) beforehand and selected the *boolean* data type. Add tags to segment the audience further and send relevant future recommendations. ### Send one more engaging push *for those who did not open the first push or did not trigger the “N minutes listened” event* Let’s send another engaging push to those not interested in the playlist to try and get them back into the app. We will invite these users to listen to the track of the day: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-10.webm "Second push notification offering track of the day to users not engaged with playlist") Note To create this push notification, we first created a [deep link](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/advanced-push-features/deep-linking/) leading to the track of the day section. Then we configured a push message containing this deep link and [saved it as a preset](https://docs.pushwoosh.com/product/content/push-presets#_top). To get the best results here, you can [personalize the content](https://docs.pushwoosh.com/product/personalization/dynamic-content#how-to-personalize-the-push) of your push notifications. If the user opens the *Track of the day* push, we will wait for the “N minutes listened” trigger to fire and continue on Journey: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-11.webm "Waiting for engagement trigger after second push notification delivery") ### Show an in-app for further engagement *for those who triggered the “N minutes listened” event* Now we want to show more engaging content to users who have listened to our playlist. First, we will create an [in-app message](https://docs.pushwoosh.com/product/content/in-apps/) with a button leading to the *Trends* section. Then we will add the **In-App** element to the canvas: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-12.webm "In-app message showing Trends section button for engaged users") ### Wait for the user to open the app *for those who did not open the second push notification* If the user does not open the second push, we will give them one more day to open the app. For this purpose, we will add the **Wait for Trigger** element and specify the default *PW_ApplicationOpen* event with a one-day wait period: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-13.webm "Wait for Trigger monitoring app opens within one day waiting period") If the user opens the app within one day, we will also show them the *Explore Trends* in-app: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-14.webm "In-app message displayed when user opens app during waiting period") Note You can tag users who have not responded to any stage of your engagement campaign. Then, after some time, you can launch a new campaign designed specifically for such users or run a similar engagement campaign with slightly modified push content. You can read more about re-engaging users [in our blog](https://blog.pushwoosh.com/blog/increase-app-engagement/#Re-engage). ### Finish the journey To end the campaign here, add the **Exit** element, or simply leave the branch without an outgoing connection. The campaign will end when the *Explore Trends* in-app appears or if the user has not responded to any step of the campaign: [](https://docs.pushwoosh.com/lifecycle-marketing-campaigns-how-to-set-up-a-user-engagement-campaign-in-pushwoosh-15.webm "Adding the Exit element to end the engagement campaign journey") ## You may also find interesting * [More about Pushwoosh Customer Journey Builder](https://www.pushwoosh.com/products/customer-journey) * [How to increase app user engagement](https://blog.pushwoosh.com/blog/increase-app-engagement/) * [7+ metrics to measure for user engagement in your app](https://blog.pushwoosh.com/blog/user-engagement-metrics/) --- # Live Activity **URL:** https://docs.pushwoosh.com/tutorials/product-guides/live-activity/ Keep flight and delivery status visible on the iOS lock screen in real time, without opening the app, using Live Activity. --- A Live Activity is an iOS lock-screen and Dynamic Island card that updates while something is in progress. The user sees status without opening the app. In Pushwoosh you start and update it from a Customer Journey with the [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/). Your iOS app must support Live Activities, and a widget schema must be published for the app. Each guide lists the events and journey steps for that scenario. [Show flight status on the lock screen ](https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-flight-status-with-live-activity)Show gate, status, and timing on the lock screen so passengers stay informed without opening the app. [Show delivery progress on the lock screen ](https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-deliveries-with-live-activity)Show courier, ETA, and distance on the lock screen so customers can follow the order without opening the app. --- # How to show delivery progress on the lock screen **URL:** https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-deliveries-with-live-activity/ Keep courier, ETA, and distance on the lock screen so customers can follow an order from placement until it arrives at the door. --- Let customers follow the courier on the lock screen from order placed until delivery. This guide builds that journey with the [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/). ## Prerequisites * Your iOS app supports Live Activities. See the [iOS SDK Live Activities guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/). * A delivery widget schema is published for this app. See [iOS Live Activity schemas configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-live-activity-schemas-configuration/). ## Create the events These are example event names for this guide. When your app or backend sends a name for the first time, Pushwoosh [creates that event automatically](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation). Create the events in the Control Panel first only if you want to set attribute types yourself before the first send. Create (or reuse) the events `OrderPlaced`, `CourierStatusChanged`, and `OrderDelivered`. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) ## Build the journey Open [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), create a campaign, and build everything below on one canvas. 1. Go to **Campaigns → Customer Journey Builder**. 2. Click **Create campaign**. 3. Click **Build a journey from scratch**. ### Set the entry 1. Add a **Trigger-based entry** element to the canvas. 2. Set it to `OrderPlaced`. ![Trigger-based entry panel with EVENT set to OrderPlaced](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-3.webp) ### Start the order card 1. Add a **Live Activity** element after the entry. 2. Name it `Order card start`. 3. Set **Action** to **Start**. 4. Select the delivery **Widget** and **Schema version**. ![Order card start with Action set to Start and delivery Widget and Schema version selected](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-1.webp) 5. Set **Card key: event** to `OrderPlaced`. ![Card key: event set to OrderPlaced](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-4.webp) 6. Fill **Card content** (`courierName`, `eta`, `distanceMeters`) and the alert fields. ![Card content fields courierName, eta, and distanceMeters filled in, with alert fields below](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-5.webp) 7. Click **Apply**. ### Wait and update the card Continue on the same canvas after the Start element. 1. Add a **Wait for Trigger** element. 2. Wait for `CourierStatusChanged`. 3. Set a waiting period that fits your delivery updates. ![Wait for Trigger set to CourierStatusChanged with a waiting period](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-6.webp) 4. On the branch that fires when the event happens, add a **Live Activity** element. 5. Name it `Order card update`. 6. Set **Action** to **Update**. 7. Choose the same **Widget** and **Schema version**. ![Order card update with Action set to Update and the same Widget and Schema version selected](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-7.webp) 8. Select `Order card start` in **Card created by**. ![Card created by set to Order card start on the Update element](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-8.webp) 9. Update **Card content**. ![Card content fields updated on the Order card update element](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-9.webp) 10. Click **Apply**. ### Wait and end the card Continue on the same canvas after the Update element. 1. Add another **Wait for Trigger** element. 2. Wait for `OrderDelivered`. ![Wait for Trigger set to OrderDelivered](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-10.webp) 3. On the branch that fires when the event happens, add a **Live Activity** element. 4. Name it `Order card end`. 5. Set **Action** to **End**. 6. Choose the same **Widget** and **Schema version**. 7. Select `Order card start` in **Card created by**. ![Order card end with Action set to End, the same Widget and Schema version selected, and Card created by set to Order card start](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-11.webp) 8. Set alert and **Dismiss after, min** if needed. ![Alert fields and Dismiss after, min on the Order card end element](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-12.webp) 9. Click **Apply**. ![Full delivery journey on the canvas with entry, Start, Wait for Trigger, Update, Wait for Trigger, and End](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-13.webp) ## What the customer sees On the lock screen, the customer gets one Live Activity card that changes as the delivery progresses. For example: * After the order is placed Courier `Alex M.`, ETA `18:30`, distance `1200` meters. ![Lock-screen Live Activity card after order placed showing courier Alex M., ETA 18, and distance 1200 meters](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-14.webp) * When courier status changes The same card updates, for example courier `Alex M.`, ETA `18:15`, distance `400` meters. ![Lock-screen Live Activity card after a courier update showing ETA 18 and distance 400 meters](https://docs.pushwoosh.com/live-activity-how-to-track-deliveries-with-live-activity-15.webp) * On delivery The card closes and leaves the lock screen. --- # How to show flight status on the lock screen **URL:** https://docs.pushwoosh.com/tutorials/product-guides/live-activity/how-to-track-flight-status-with-live-activity/ Keep gate, status, and timing on the lock screen for checked-in passengers until the flight lands and the card closes on its own. --- Show each checked-in passenger gate, status, and timing on the lock screen until the flight lands. This guide builds that journey with the [Live Activity element](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/live-activity/). ## Prerequisites * Your iOS app supports Live Activities. See the [iOS SDK Live Activities guide](https://docs.pushwoosh.com/developer/pushwoosh-sdk/ios-sdk/ios-live-activities/). * A flight widget schema is published for this app. See [iOS Live Activity schemas configuration](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-live-activity-schemas-configuration/). ## Create the events These are example event names for this guide. When your app or backend sends a name for the first time, Pushwoosh [creates that event automatically](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation). Create the events in the Control Panel first only if you want to set attribute types yourself before the first send. Create (or reuse) the events `CheckInCompleted`, `FlightStatusChanged`, and `FlightLanded`. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) ## Build the journey Open [Customer Journey Builder](https://docs.pushwoosh.com/product/customer-journey/pushwoosh-journey-overview/), create a campaign, and build everything below on one canvas. 1. Go to **Campaigns → Customer Journey Builder**. 2. Click **Create campaign**. 3. Click **Build a journey from scratch**. ### Set the entry 1. Add a **Trigger-based entry** element to the canvas. 2. Set it to `CheckInCompleted`. ![Trigger-based entry panel with EVENT set to CheckInCompleted](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-3.webp) ### Start the flight card 1. Add a **Live Activity** element after the entry. 2. Name it `Flight card start`. 3. Set **Action** to **Start**. 4. Select the flight **Widget** and **Schema version**. ![Flight card start with Action set to Start and flight Widget and Schema version selected](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-1.webp) 5. Set **Card key: event** to `CheckInCompleted`. ![Card key: event set to CheckInCompleted](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-4.webp) 6. Fill **Card content** (`gate`, `status`, `estimatedTime`) and the alert fields. ![Card content fields gate, status, and estimatedTime filled in, with alert fields below](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-5.webp) 7. Click **Apply**. ### Wait and update the card Continue on the same canvas after the Start element. 1. Add a **Wait for Trigger** element. 2. Wait for `FlightStatusChanged`. 3. Set a waiting period that fits your flight updates. ![Wait for Trigger set to FlightStatusChanged with a waiting period](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-6.webp) 4. On the branch that fires when the event happens, add a **Live Activity** element. 5. Name it `Flight card update`. 6. Set **Action** to **Update**. 7. Choose the same **Widget** and **Schema version**. ![Flight card update with Action set to Update and the same Widget and Schema version selected](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-7.webp) 8. Select `Flight card start` in **Card created by**. ![Card created by set to Flight card start on the Update element](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-8.webp) 9. Update **Card content**. ![Card content fields updated on the Flight card update element](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-9.webp) 10. Click **Apply**. ### Wait and end the card Continue on the same canvas after the Update element. 1. Add another **Wait for Trigger** element. 2. Wait for `FlightLanded`. ![Wait for Trigger set to FlightLanded](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-10.webp) 3. On the branch that fires when the event happens, add a **Live Activity** element. 4. Name it `Flight card end`. 5. Set **Action** to **End**. 6. Choose the same **Widget** and **Schema version**. 7. Select `Flight card start` in **Card created by**. ![Flight card end with Action set to End, the same Widget and Schema version selected, and Card created by set to Flight card start](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-11.webp) 8. Set alert and **Dismiss after, min** if needed. ![Alert fields and Dismiss after, min on the Flight card end element](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-12.webp) 9. Click **Apply**. ![Full flight status journey on the canvas with entry, Start, Wait for Trigger, Update, Wait for Trigger, and End](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-13.webp) ## What the passenger sees On the lock screen, the passenger gets one Live Activity card that changes as the flight progresses. For example: * After check-in Gate `B12`, status `Boarding`, estimated time `18:45`. ![Lock-screen Live Activity card after check-in showing gate B12, Boarding, and estimated time 18](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-14.webp) * When status changes The same card updates, for example gate `B14`, status `Delayed`, estimated time `19:10`. ![Lock-screen Live Activity card after a status change showing gate B14, Delayed, and estimated time 19](https://docs.pushwoosh.com/live-activity-how-to-track-flight-status-with-live-activity-15.webp) * After landing The card closes and leaves the lock screen. --- # Wallet pass guides **URL:** https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/ Guides to issue Apple and Google Wallet passes in a Customer Journey and to message members who hold your pass about a reward. --- A Wallet pass is a digital card, such as a loyalty card or coupon, that users keep in Apple Wallet or Google Wallet. Pushwoosh can issue it from a journey and deliver the one-tap install link in a later message. Use these guides to build a Customer Journey around the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element. Pick the scenario that matches your product. [How to issue a wallet pass on signup ](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-signup)Issue a loyalty pass when a member joins and deliver the install link in the welcome email. [How to issue a wallet pass on geozone entry ](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-geozone-entry)Issue a pass when a shopper enters a store geozone and push the install link for their platform. [How to notify wallet pass holders about a reward ](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-notify-wallet-pass-holders-about-a-reward)Send members who hold your pass a reward message that appears on the pass itself. --- # How to issue a wallet pass on geozone entry **URL:** https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-geozone-entry/ Learn how to issue a wallet pass when a shopper enters a store geozone, then push the platform-specific install link to them. --- Issue an Apple or Google Wallet pass when a shopper enters a store geozone, then push the install link. This guide builds that journey with the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element. Prerequisites * Complete [Apple Wallet pass setup](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/), and/or configure [Google Wallet for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/), for the platforms you support. * Set up [iOS push notifications](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/) and/or [Android push notifications](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/). The journey sends the install link in a **Push** element. * Geo campaigns need location permission on the device and location tracking enabled in the app. See [Understanding geo campaigns](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/). ## Create a Wallet Pass Campaign The [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element can only issue passes from an existing campaign, so create one first. A campaign’s platform is permanent, so if you support both Apple Wallet and Google Wallet, repeat this section once per platform. 1. Go to **Campaigns → Wallet Passes → Campaigns** and click **Create campaign**. 2. Choose **Apple Wallet** or **Google Wallet** as the **Platform**, enter a **Name** for your own reference, for example `Store loyalty` (this labels the campaign in Pushwoosh, it doesn’t appear on the pass), and pick a pass style, for example **Store Card** (Apple) or **Loyalty card** (Google). ![New campaign creation form with Name set to Store loyalty and Pass Style set to Store Card](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-7.webp) 3. Under **Basic Information**, set **Pass Name** to what shows on the pass itself, for example `Store Loyalty Card`. ![Basic information section with Pass Name set to Store Loyalty Card](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-8.webp) 4. Design the rest of the pass, then preview and create the campaign. Full field-by-field walkthrough: [Create a Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/). ## Create the store’s geozone The journey needs a live geozone to trigger from, so create one before building the journey. 1. Go to **Campaigns → Geo campaigns** and click **Create geozone**. ![Geo campaigns Get Started screen with map view and Create geozone action in the Pushwoosh control panel](https://docs.pushwoosh.com/push-notifications-geo-based-push-1.webp) 2. Enter a **Campaign name**, for example `Downtown store`. ![Geozone form section with Campaign name set to Downtown store](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-4.webp) 3. Under **Trigger location**, choose **Circle**, search for the store’s address, and set **Range, m** (at least 50). ![Trigger location set to Circle mode with a 200 m range circle drawn around a Downtown Los Angeles address](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-5.webp) 4. Under **Entry actions**, turn on [**Fire event**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#fire-event). Leave **Event name** empty so the default `PW_GeozoneEntered` is used. 5. Turn [**Send push**](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#send-push) off if you only want the journey to message the shopper, not a separate geozone push on the same entry. 6. Set **Cooldown** (**Standalone** with a short duration is enough to start) and **Campaign schedule** (**Always active** unless you need set hours). ![Cooldown mode dropdown set to Standalone with a 1-hour duration](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-6.webp) 7. Click **Save**. The event is available in **Trigger-based entry** as soon as you save. Full field-by-field walkthrough: [Create a geozone](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/). Note `PW_GeozoneEntered` fires when a device enters and the geozone’s entry actions are allowed to run (campaign schedule active, [cooldown](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#choose-a-cluster-and-cooldown) allows delivery, and the user granted location permission). ## Build the journey Go to **Campaigns → Customer Journey Builder**, click **Create campaign**, and build everything below on one canvas. ### Set the entry 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element to the canvas. 2. Name it `Geozone Entry`. 3. Select `PW_GeozoneEntered` as the **Event**. Limit to one store This guide uses the default event name. Every geozone that leaves **Event name** empty fires the same `PW_GeozoneEntered`, so shoppers from all those stores enter this journey. To limit it to one store, set a unique **Event name** on that geozone and select that name here, or keep the default and click **+ Add condition** to filter by `geozone_code` (or `geozone_name`) for that store. ### Issue the wallet pass 1. Add **Issue wallet pass** right after the entry. 2. Name it `Store pass`. ![Geozone Entry trigger node connected to a Store pass Issue Wallet Pass node on the journey canvas](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-1.webp) 3. In **Apple Wallet campaign**, select the campaign you created for Apple. In **Google Wallet campaign**, select the campaign you created for Google. Each dropdown only lists campaigns for its own platform. The element issues a pass for each platform you select. ![Issue Wallet Pass settings with Apple Wallet campaign and Google Wallet campaign as two separate dropdowns, both set to Store loyalty](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-11.webp) ### Branch by platform and send the push Note You could also branch on the **Platform** [tag](https://docs.pushwoosh.com/product/audience-data-and-segmentation/user-data-tags/tags/) with a [Condition split](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/segment-split/), but a stored tag value doesn’t confirm the device can currently receive a push. Use **Reachability check** instead: it confirms the device is registered and **Push Alerts Enabled** is on, checking only the device that entered when you set that scope. 1. After **Issue wallet pass**, add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) element and name it, for example, `iOS Device?`. 2. Under **Push Notifications**, click **iOS**. ![Push Notifications channel with iOS selected among the platform chips](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-9.webp) 3. Under **Which devices to check**, select **Only the device that entered**, so the check matches the device that triggered the geozone entry, not every device the shopper owns. ![Which devices to check set to Only the device that entered](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-10.webp) 4. On the **Push: iOS** branch, add a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element. Select **Marketing message** or **Transactional message**, and turn off **Send to all user devices (from UserID)** so the push goes only to the device that entered the geozone. 5. In the message body, insert `{apple_install_link|regular|}`. 6. Turn on **Personalise message with event attributes**, check **Overwrite placeholder**, set **Source event** to `Store pass`, and set **Dynamic attribute** to `apple_install_link`. ![Overwrite placeholder checked with apple_install_link, Source event set to Store pass and Dynamic attribute set to apple_install_link](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-12.webp) 7. After you edit the push, reselect the preset so the mapping picks up the new placeholder. Full click path: [Send the pass link to the user](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/#send-the-pass-link-to-the-user). 8. On **All others**, add a second **Reachability check** named `Android Device?`, with **Push Notifications** narrowed to **Android** and the same **Only the device that entered** setting. 9. On its **Push: Android** branch, add **Push**. Set the message type and turn off **Send to all user devices (from UserID)** as on the iOS branch. Insert `{google_save_link|regular|}` and map **Dynamic attribute** `google_save_link` the same way. 10. Leave the remaining **All others** branch unconnected, unless you have another channel for those users. A branch with no outgoing connection ends the journey there on its own, so it doesn’t need an **Exit** element. ![Assembled journey with Geozone Entry, Store pass, two Reachability checks, and a Push element with the pass link on each branch](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-geozone-entry-3.webp) A shopper reaches **All others** when their device isn’t registered on the platform you’re checking, or when their **Push Alerts Enabled** tag isn’t `true` for that device (push permission off). When `PW_GeozoneEntered` fires, the shopper enters the journey, **Issue wallet pass** issues their pass, and the matching **Push** delivers the install link for their platform. ### Launch and check 1. Launch the journey when the canvas is ready. 2. After a test entry, check entries and drop-offs on **Issue wallet pass** and the stats on each **Push**. ### Later visits A repeat visit reuses the same pass and resends the push. Only the geozone’s [cooldown](https://docs.pushwoosh.com/product/messaging-channels/push-notifications/geo-campaigns/create-geozone/#choose-a-cluster-and-cooldown) gates a later visit, not the entry’s [re-entry setting](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/#control-whether-users-can-re-enter-the-campaign). That setting only blocks a repeat trigger while the shopper’s previous session is still active, and the session ends right after the first visit’s push, since neither branch connects to a further step. So the default **Do not allow re-entry** doesn’t stop a later visit. See [What happens at this step](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/#what-happens-at-this-step) for the pass-issuance side. --- # How to issue a wallet pass on signup **URL:** https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-signup/ Learn how to issue an Apple or Google Wallet pass on signup and deliver the install link in a Customer Journey welcome email. --- Issue an Apple or Google Wallet pass when a loyalty member joins, then deliver the install link by email. This guide builds that journey with the [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element. Prerequisites * Complete [Apple Wallet pass setup](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/), and/or configure [Google Wallet for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/), for the platforms you support. ## Create a Wallet Pass Campaign The [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element can only issue passes from an existing campaign, so create one first. A campaign’s platform is permanent, so if you support both Apple Wallet and Google Wallet, repeat this section once per platform. 1. Go to **Campaigns → Wallet Passes → Campaigns** and click **Create campaign**. 2. Choose **Apple Wallet** or **Google Wallet** as the **Platform**, enter a **Name** for your own reference, for example `Coffee discount` (this labels the campaign in Pushwoosh, it doesn’t appear on the pass), and pick a pass style, for example **Coupon** (Apple) or **Loyalty card** (Google). ![Campaign section with Name set to Coffee discount and Pass Style set to Coupon](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-5.webp) 3. Under **Basic Information**, set **Pass Name** to what shows on the pass itself, for example `Coffee Shop`. ![Basic Information section with Pass Name set to Coffee Shop](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-6.webp) 4. Add a field to carry the offer. 1. On Apple Wallet, under **Pass Fields**, click **Add Field** under **Primary Fields**. 2. Set its **Key (ID)** to `offer`. 3. Set its **Label** to `OFFER`. Google Wallet’s pass types use their own named fields instead of free-form ones. See [Apple Wallet pass fields](https://docs.pushwoosh.com/product/content/wallet-passes/apple-wallet-pass-builder/#fill-in-pass-fields) or [Google Wallet fields](https://docs.pushwoosh.com/product/content/wallet-passes/google-wallet-pass-builder/#fill-in-pass-fields) for the exact field names your pass style offers. ![Primary Fields section with an Add Field card showing Key (ID) offer, Label OFFER, and Value 10% OFF](https://docs.pushwoosh.com/wallet-pass-campaigns-add-field.webp) 5. In **Shared and personal fields**, that field now appears under its Label. Mark it **Personal**. Later, **Issue wallet pass** lists it by its Key (ID), shown in capitals (`OFFER`), and fills it with each member’s own `offer_code`. ![Shared and personal fields configuration section with Personal toggles next to each field](https://docs.pushwoosh.com/wallet-pass-campaigns-3.webp) 6. Preview and create the campaign. Full field-by-field walkthrough: [Create a Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/create-a-campaign/). ## Create the signup event 1. Go to **Audience → Events**, click **Create event**, and choose **Custom Event** (not **Default Event**). ![Create Event dropdown menu showing Custom Event option](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-event-1.webp) 2. Name it `CoffeeClubJoined` and add an attribute for the welcome offer, for example `offer_code`. ![Event Name field and an attribute row with Name and Type fields highlighted](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-event-2.webp) 3. Ask development to send that event when a member joins, with the member’s User ID and `offer_code`. The pass is issued to that User ID, so send it once the member’s User ID is set. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) ## Build the journey Go to **Campaigns → Customer Journey Builder**, click **Create campaign**, and build everything below on one canvas. ### Set the entry 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element to the canvas. 2. Select `CoffeeClubJoined` as the **Event**, so every new member enters as soon as they join. ![Trigger-based entry settings with Event set to CoffeeClubJoined](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-1.webp) ### Issue the wallet pass 1. Add an [Issue wallet pass](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/) element after the entry. 2. Name it `Loyalty card`. ![Issue Wallet Pass Step Name field set to Loyalty card](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-2a.webp) 3. In **Apple Wallet campaign**, select the campaign you created for Apple. In **Google Wallet campaign**, select the campaign you created for Google, if you support Android. Each dropdown only lists campaigns for its own platform. ![Apple Wallet campaign set to Coffee discount and Google Wallet campaign left empty](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-2b.webp) 4. Under **Personal fields on the pass**, the list shows the personal field(s) from the campaign(s) you selected. Set **OFFER** (or the field you added on Google Wallet, if you support it) to the trigger entry event and choose `offer_code` as the **Attribute**, so each member’s pass carries their own welcome offer. ![Personal fields on the pass with OFFER mapped to the offer_code attribute](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-2c.webp) ### Deliver the pass by email 1. Add an [Email](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/email/) element right after **Issue wallet pass** and name it `Welcome email`. 2. In the message body, insert the install link with vertical bars, for example `{apple_install_link|regular|}` (or `{google_save_link|regular|}`). Do not use a plain `{apple_install_link}`. ![Email body with an Add to Apple Wallet link next to the apple_install_link placeholder](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-email-body.webp) 3. Turn on **Personalise message with event attributes**. 4. Check **Overwrite placeholder**, set **Source event** to your **Issue wallet pass** **Step Name** (`Loyalty card`), and set **Dynamic attribute** to match the placeholder. Full click path: [Send the pass link to the user](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/issue-wallet-pass/#send-the-pass-link-to-the-user). ![Personalise message with event attributes table mapping apple_install_link to the Loyalty card step](https://docs.pushwoosh.com/journey-elements-issue-wallet-pass-11.webp) Note The element issues a pass only for the platform(s) you configured, not based on the member’s device. Match the install link in the email to the campaign you selected (`apple_install_link` for Apple, `google_save_link` for Google). To deliver by push and branch by platform instead, see [How to issue a wallet pass on geozone entry](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-geozone-entry/#branch-by-platform-and-send-the-push). When `CoffeeClubJoined` fires, the member enters the journey, **Issue wallet pass** issues their pass with that event’s `offer_code`, and **Welcome email** sends the welcome message with the link to add it. ![Assembled coffee shop loyalty journey with Trigger entry, Loyalty card, and Welcome email connected in sequence](https://docs.pushwoosh.com/how-to-issue-a-wallet-pass-on-signup-4.webp) ### Launch and check 1. Launch the journey when the canvas is ready. 2. After a test signup event, check entries and drop-offs on **Issue wallet pass** and the stats on **Welcome email**. --- # How to notify wallet pass holders about a reward **URL:** https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-notify-wallet-pass-holders-about-a-reward/ Learn how to send loyalty members who hold your Apple or Google Wallet pass a reward message that appears on the pass itself. --- Tell loyalty members that a reward is ready the moment they earn it, with a message that appears on their Apple Wallet or Google Wallet pass. In this guide, you build a Customer Journey that sends the message only to members who have added your pass. Prerequisites * Complete [Apple Wallet pass setup](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/ios-configuration/ios-wallet-pass-configuration/), and/or configure [Google Wallet for Android](https://docs.pushwoosh.com/developer/first-steps/connect-messaging-services/android-configuration/android-google-wallet-configuration/), for the platforms you support. * Issue your loyalty pass to members by [User ID](https://docs.pushwoosh.com/product/pushwoosh-knowledge-hub/users-userids/), in the same application as this journey, for example from a [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/issue-passes/) or with [How to issue a wallet pass on signup](https://docs.pushwoosh.com/tutorials/product-guides/wallet-passes/how-to-issue-a-wallet-pass-on-signup/). ## Create the reward event An event is a signal that your system sends when something happens to a user. Here, it’s sent when a member earns a reward, for example when they reach 100 loyalty points or collect the tenth stamp on a coffee card. 1. Go to **Audience → Events**, click **Create event**, and choose **Custom Event**. 2. Name it `RewardUnlocked`. The event needs no attributes, because the message text is the same for every member. 3. Ask development to send the event when a member earns a reward. [Learn more about creating events](https://docs.pushwoosh.com/product/audience-data-and-segmentation/events/#implementation) Developer assistance needed Send `RewardUnlocked` with the [`postEvent`](https://docs.pushwoosh.com/developer/api-reference/user-centric-api/#postevent) API call or the SDK, and pass the member’s User ID in it. Use the same User ID the pass was issued to, otherwise the journey can’t find the member’s pass. ## Prepare the message Create a [push preset](https://docs.pushwoosh.com/product/content/push-presets/) with the reward message, for example: * **Title:** `Riverside Café Rewards` * **Text:** `Your free coffee is ready. Show your loyalty card at the counter.` Always fill in the text. The pass shows it, and on Apple Wallet a push without text only refreshes the pass, so the holder sees nothing. What the holder sees depends on the wallet: * **Apple Wallet:** a notification from the pass with the iOS text of the push, under the push title. Pushwoosh removes the message from the pass after about an hour. Apple Wallet notifies only when the text changes, so a second push with the same text within that hour doesn’t show a notification. * **Google Wallet:** an Android notification with the push title and text, and the message on the pass. The pass keeps only the latest message. Google also limits how often a pass can notify its holder. See [Google Wallet notification](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#google-wallet-notification). ## Build the journey Go to **Campaigns → Customer Journey Builder**, click **Create campaign**, and build everything below on one canvas. ### Set the entry 1. Add a [Trigger-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/trigger-based-entry/) element to the canvas. 2. In **Point name**, enter `Reward unlocked`. 3. Under **Event**, select `RewardUnlocked`. ![Trigger-based entry settings with Point name Reward unlocked and Event set to RewardUnlocked](https://docs.pushwoosh.com/how-to-notify-wallet-pass-holders-about-a-reward-1.webp) ### Keep only members who hold the pass 1. Add a [Reachability check](https://docs.pushwoosh.com/product/customer-journey/journey-elements/flow-controls/reachability-check/) element after the entry and name it `Holds the pass?`. 2. Under **Reachable by**, click **Wallet Pass** in **Add a channel**, then delete the **Push Notifications** channel the element starts with. 3. Keep **All platforms** selected to check Apple Wallet and Google Wallet passes. 4. Under **Which devices to check**, select **All devices of the user**. The pass is registered as a separate device of the member, so checking only the device that sent the event would miss it. ![Reachability check with the Wallet Pass channel for all platforms and All devices of the user selected](https://docs.pushwoosh.com/how-to-notify-wallet-pass-holders-about-a-reward-2.webp) The element gets two branches. Members who have added your pass go to **Wallet**, and everyone else goes to **All others**. ### Send the reward message 1. On the **Wallet** branch, add a [Push](https://docs.pushwoosh.com/product/customer-journey/journey-elements/channels/push/) element and name it `Reward message`. 2. In **Preset**, select the preset you prepared. 3. Select **Transactional message**. The member earned the reward and expects to hear about it, so the message reaches everyone who holds the pass, including members in the global control group and those who hit the frequency limit. Learn more about [message types](https://docs.pushwoosh.com/product/messaging-channels/marketing-vs-transactional/). ![Push settings with the Riverside Café reward unlocked preset, its preview, and Transactional message selected](https://docs.pushwoosh.com/how-to-notify-wallet-pass-holders-about-a-reward-3.webp) 4. Keep **Send to all user devices (from UserID)** turned on. The push then goes to every device tied to the member’s User ID, including the pass and, for example, the phone with your app. With the setting turned off, the push goes only to the device that sent the event, and the pass gets nothing. ![Push delivery settings with Send to all user devices (from UserID) turned on and highlighted](https://docs.pushwoosh.com/how-to-notify-wallet-pass-holders-about-a-reward-4.webp) 5. Optionally, add an **Exit** element after **Reward message**, and connect the **All others** branch to another **Exit** element. When `RewardUnlocked` fires, the member enters the journey. If they have added your pass, **Reward message** sends the push to all their devices, and the wallet shows it on the pass. Members who don’t hold the pass leave the journey through **All others** and get no message. A member with no device at all in the application drops out at **Holds the pass?** with the [Device not found](https://docs.pushwoosh.com/product/troubleshooting/drop-offs-in-customer-journey/#device-not-found) reason. ![Reward journey with Reward unlocked, the Holds the pass? check, Reward message, and two Exit elements on the canvas](https://docs.pushwoosh.com/how-to-notify-wallet-pass-holders-about-a-reward-5.webp) ### Launch and check 1. Launch the journey when the canvas is ready. 2. Ask your developer to send a test `RewardUnlocked` event for a member who holds the pass. 3. On **Holds the pass?**, check how many users went to **Wallet** and **All others**. 4. On **Reward message**, check the stats. ## Update the pass content (optional) The push doesn’t change the fields of your pass design. On Apple Wallet, the push title shows in place of the pass’s organization name until the message is removed. To show the reward on the card itself, for example a new points balance, update the pass separately: * In the Control Panel, with **Edit** in [All passes](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/manage-passes/#view-and-manage-existing-passes), or with a design change in the pass’s [Wallet Pass Campaign](https://docs.pushwoosh.com/product/messaging-channels/wallet-passes/wallet-pass-campaigns/manage-a-campaign/). * Through the [Apple PassKit API](https://docs.pushwoosh.com/developer/api-reference/apple-wallet-passkit-api/) or the [Google Wallet API](https://docs.pushwoosh.com/developer/api-reference/google-wallet-api/). ## Other ways to start the journey * **[Audience-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/audience-based-entry/):** send the message to a whole segment once or on a schedule, for example a weekly offer to members of your loyalty program. * **[API-based entry](https://docs.pushwoosh.com/product/customer-journey/journey-elements/entry-elements/api-based-entry/):** start the journey from your own system, for example when an event’s start time changes for everyone holding a ticket.