Skip to main content
In-app messages appear inside your app UI. This guide covers fetching eligible messages, rendering them, and reporting impressions to Masivo.

Prerequisites

  • An active Masivo account with at least one ACTIVE in-app template
  • A SERVER or CLIENT API key
  • Customers registered with external_id values matching your app user ids
  • For real-time journey delivery: a registered FCM device token and push consent on the customer profile (same setup as push notifications). Without them, messages still materialize in Masivo but the app only sees them on the next GET /inapp-messages fetch (for example on app open).

Check capabilities

Before wiring the SDK, confirm in-app is enabled and read journey event types that support real-time refresh:
GET /api/storefront/v1/inapp/capabilities
Authorization: Bearer <API_KEY>
x-account-id: <ACCOUNT_ID>
Example response:
{
  "data": {
    "inappEnabled": true,
    "eventTypes": ["PURCHASE", "APP_OPEN"]
  }
}
When inappEnabled is false, skip in-app fetch calls until templates are published.

Fetch eligible messages

Call this endpoint after app launch and whenever a trigger event from eventTypes fires:
GET /api/storefront/v1/customers/{customer_id}/inapp-messages?limit=5
Authorization: Bearer <API_KEY>
x-account-id: <ACCOUNT_ID>
Optional query parameters:
ParameterDescription
brand_idFilter by brand
triggerEvent type active on screen (returns untriggered or matching messages)
typesComma-separated types, for example modal,banner
limitMax messages (1–50, default 10)
Example response:
{
  "data": {
    "messages": [
      {
        "id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
        "template_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
        "type": "modal",
        "priority": 10,
        "expires_at": "2026-08-01T00:00:00Z",
        "content": {
          "title": "Welcome back",
          "body": "You have 500 points ready to redeem.",
          "image_url": "https://cdn.example.com/hero.png",
          "buttons": [
            {
              "id": "primary",
              "text": "Redeem now",
              "action": "deeplink",
              "value": "myapp://rewards"
            },
            {
              "id": "dismiss",
              "text": "Not now",
              "action": "dismiss"
            }
          ]
        },
        "display_rules": {
          "max_impressions": 3,
          "priority": 10
        },
        "trigger": {
          "event": "APP_OPEN"
        }
      }
    ]
  }
}

Render messages

1

Sort by priority

Messages are pre-sorted by Masivo, but you can re-sort using display_rules.priority when showing multiple surfaces.
2

Map type to UI

Render modal, banner, slideup, or fullscreen using your design system. Use content.buttons for actions.
3

Respect display rules

Honor max_impressions client-side as a fallback, but always log events so Masivo enforces caps server-side.

Log impression events

After displaying or interacting with a message, report the action:
POST /api/storefront/v1/customers/{customer_id}/inapp-messages/{message_id}/events
Authorization: Bearer <API_KEY>
x-account-id: <ACCOUNT_ID>
Content-Type: application/json

{
  "action": "shown"
}
Supported actions: shown, clicked, dismissed. For button clicks, include metadata:
{
  "action": "clicked",
  "metadata": {
    "button_id": "primary"
  }
}
Masivo returns { "data": { "success": true } } and emits INAPP_SHOWN, INAPP_CLICKED, or INAPP_DISMISSED tracking events.

Common patterns

Refresh on app open

const refreshInApp = async (customerId: string) => {
  const res = await fetch(
    `https://app.masivo.ai/api/storefront/v1/customers/${customerId}/inapp-messages`,
    {
      headers: { Authorization: `Bearer ${apiKey}`, "x-account-id": accountId }
    }
  );
  const { data } = await res.json();
  return data.messages;
};

Refresh after a journey trigger event

const onEvent = async (customerId: string, eventType: string) => {
  const caps = await fetchCapabilities();
  if (!caps.eventTypes.includes(eventType)) return;
  const query = new URLSearchParams({ trigger: eventType });
  const res = await fetch(
    `https://app.masivo.ai/api/storefront/v1/customers/${customerId}/inapp-messages?${query}`,
    {
      headers: { Authorization: `Bearer ${apiKey}`, "x-account-id": accountId }
    }
  );
  const { data } = await res.json();
  showNextMessage(data.messages[0]);
};

Log shown before render

Always send shown after the message is visible. Masivo assigns a tracking_id on shown and reuses it for subsequent clicked or dismissed events in the same impression.

Frequently asked questions

Displaying messages does not — your app fetches eligible messages over HTTPS and renders them in your UI.Real-time journey delivery does. When a journey materializes an in-app message, Masivo sends a silent push so the app refreshes its queue. That path requires:
  • A registered FCM device token on the customer (PATCH /customers/{id}/devices)
  • Push consent on the customer profile (consent.purposes.push_notifications not disabled)
  • Firebase configured in Push settings
If either is missing, Masivo skips the silent push. The message remains in the database and appears on the next fetch (for example when the user opens the app).
The message is excluded from GET /inapp-messages results. A final shown event moves the row out of pending when the cap is hit.
Yes, when display_rules.max_impressions is greater than 1 and min_interval_seconds has elapsed since the last shown event.
When a qualifying event is processed, Masivo materializes the in-app message and sends a silent push to devices with a valid FCM token and push consent. Your app handles that payload, then calls GET /inapp-messages (optionally with the trigger query param from the push data). Check eventTypes from /inapp/capabilities for supported journey events.