Personalize messages

Use a saved first name in a greeting, or the user's latest watched show in a reminder. The same field picker works across push notifications, in-app messages, feedback and surveys. Your app supplies the data; UserGist resolves it for each recipient and passes navigation data back to your app when an action is tapped.

This guide covers the React Native and web integrations. Web supports in-app messages, feedback and surveys; push notifications use the native integration.

Choose the right source

Source in Insert fieldUse it forMovie example
User propertyA current value your app or backend maintains.first_name, country, or the current resume_show_id.
Latest matching activityThe most recent matching event within a chosen lookback.The title, ID and position from the latest show_watched event.
Triggering eventThe exact event that caused this message.A reminder about the show in this particular request, even if later activity changes.
Transactional send dataValues supplied with a supported server-side send.A backend-selected show for this send.
AppApp metadata.name.
Current timeThe resolution timestamp.iso.

For latest activity, choose a lookback of 1–90 days and optional property filters. Filters compare typed values: boolean false differs from the string "false". Historical events must be present in the personalization event store.

Transactional send fields require the data object on the transactional push API. They are not automatically populated on ordinary event or scheduled sends. App and current-time fields are available when you need those values; date localization is not part of this field renderer.

Last watched and continue watching are different decisions

A filter such as completed = false finds a historical event with that value. It does not remove that event when the same show is completed later. For a production continue-watching feature, have your backend maintain the current eligible resume title, ID and position together as user properties, after checking completion and availability. Use latest activity when historical recency is the decision you want. UserGist does not join an external movie catalog.

Send profile and activity data

After initialization and analytics consent, update the active React Native user:

import { UserGist } from '@usergist/feedback-react-native'
 
const result = await UserGist.setUserProperties({
  first_name: 'Ava',
  country: 'IL',
})
// result: 'synced', 'queued' for retry, or 'rejected'.
 
UserGist.track('show_watched', {
  show_id: '00123',
  show_title: 'Midnight Orbit',
  position_seconds: 1234,
  completed: false,
})
 
// Explicitly remove a saved property when it is no longer known.
await UserGist.setUserProperties({}, ['first_name'])

The web client exposes the same client.setUserProperties(values, unsetKeys?) and client.track(name, values) methods after explicit activation. Its property update returns a promise and uses the durable retry queue; it does not return the React Native status string. Wait for queued data to synchronize before expecting a server preview to use it.

Properties are flat strings, finite numbers, booleans or null. Use strings for IDs with meaningful leading zeros and ISO date strings for date fields. A dotted key is a literal property name, not a nested-object path. Send related current resume properties in one update. See property update APIs for backend-owned values and idempotent retries.

Anonymous users, identity and privacy

An anonymous user can have saved properties and activity without an account ID. Secure identification links the anonymous alias to the identified user. Reset the SDK before switching accounts; client updates remain bound to the authenticated user. See the React Native and web identity instructions.

Profile updates and tracked activity require analytics consent. Delivery also requires the relevant channel consent. Sensitive keys such as first_name must be permitted by the app's exact privacy allow-list. The picker marks filtered fields unavailable. A fallback can handle an absent name; changing the property key to bypass privacy settings is not a solution.

Insert a greeting or movie field

  1. In the campaign composer, place the cursor in a supported text field and choose Insert field. Choose User property, property key first_name, value type string, display name First name, and fallback there.
  2. Insert the field, then complete the greeting. The editor displays a field chip; its display name is for authors and does not rename the customer's property.
  3. To add a movie title, choose Latest matching activity, event show_watched, and name the source Last watched show. Choose show_title with type string and a suitable lookback.
  4. When adding show_id and position_seconds, choose Last watched show · reuse this source. All three values then come from one selected event. Choose Use an existing field when you want to insert an already-defined field again, such as the same title in a button label.
  5. Open Fields to review field definitions and edit labels or fallbacks.

For a triggering-event example, configure an event-triggered campaign and choose Triggering event as the field source. Send the required movie properties on that triggering event. Typing an event name in the field picker helps discover properties; configure the actual delivery trigger in the campaign's Target step.

The picker groups source settings, property selection, display name/type and fallback. Dropdowns and text inputs share the dashboard's standard field height. Action editors group Button label and Behavior, followed by the destination or Action data appropriate to that behavior.

Open the correct show when tapped

Personalized text alone does not tell an app which screen to open. Supply the show ID and playback position in a JSON action, and handle that action in your app.

For a push or in-app button, choose Behavior → In-app JSON. For tapping the notification itself, configure its default tap action as JSON too. In Action data, use Insert field for each dynamic value. This example shows the stored template with illustrative binding IDs; use the tokens created by your picker, or define matching bindings when authoring through the API:

{
  "type": "open_show",
  "show_id": "{{p.show_id}}",
  "position_seconds": "{{p.position_seconds}}"
}

Set the show ID binding to string and position to number. Although a token is quoted in the template so it remains valid JSON, a whole-value token preserves its declared type when resolved. The app receives:

{
  "type": "open_show",
  "show_id": "00123",
  "position_seconds": 1234
}

A token embedded in longer text produces a string. Insert fields into JSON values, not keys, and resolve preview to inspect the final object.

Register React Native handlers once, alongside your existing handlers:

import { Push, UserGist } from '@usergist/feedback-react-native'
 
function openMovie(action: Record<string, unknown>) {
  if (
    action.type !== 'open_show' ||
    typeof action.show_id !== 'string' ||
    typeof action.position_seconds !== 'number' ||
    !Number.isFinite(action.position_seconds) ||
    action.position_seconds < 0
  ) return
 
  // Your app checks access/availability and opens its own player.
  navigateToPlayer(action.show_id, action.position_seconds)
}
 
UserGist.setInAppHandlers({ onJsonAction: openMovie })
Push.setHandlers({ onJsonAction: openMovie })

For web, pass onAction: openMovie in the client's init configuration. A deep link or URL is another option if your app already supports it; personalize its destination and verify the resolved URL. UserGist does not create the player or grant access to a movie.

Preview and missing values

Choose Preview as user, select Anonymous ID, Identified user ID or UserGist subject ID, and paste the demo user's matching ID. For fields from a triggering event, supply that user's triggering event ID. Choose Resolve preview to inspect the message, resolved values and action data.

Preview does not send a notification, create a survey response or consume a frequency limit. A preview is not a delivery reservation: a later send can use newer profile or latest-activity data. Changing the recipient or content clears the old preview.

ResultMeaning
readyRequired fields resolved with the expected types.
using_fallbackAn absent value used its explicitly configured fallback.
skippedA required value is unavailable, has the wrong type, or creates invalid content. Inspect the reported field/path.

Absent, null or blank text can use a fallback. A supplied value of the wrong type must be corrected at its source; a fallback does not silently convert it. Missing optional artwork is omitted. Keep destination IDs required when opening an unrelated default movie would be misleading.

Each logical push delivery keeps a recipient snapshot for retries, and a survey attempt keeps its resolved questions. Later activity does not rewrite an attempt already in progress. A new send or attempt can resolve newer values.

Delivery timing and surveys

For feedback, surveys and in-app messages, the Target step follows Audience → Platforms → Trigger. Select one or more of the platforms enabled in this app's settings. Web is hidden when it is not enabled; an enabled platform is available before its first SDK connection so you can prepare campaigns.

Platforms describe where the experience appears. The SDK framework is configured separately: React Native, Expo and Flutter provide iOS and Android choices. Select both to reach both app versions, or just one for an OS-specific campaign. Web covers mobile and desktop browsers. Existing campaigns keep their saved selection; enabling Web does not add it to an existing campaign automatically. If a saved selection is later disabled in app settings, the composer explains it and lets you remove it explicitly after selecting an available platform.

With the immediate-delivery API and SDK release, React Native flushes known server-dependent triggers as they occur, and the API can return authorized content in that response. Web consumes the same immediate response path. Selecting web plus mobile does not by itself require waiting for the normal analytics batch or the next instruction poll. Polling remains a recovery path.

Personalized content and cross-device eligibility still require an online server decision. Network latency, another open experience, configured delays and frequency rules can affect when content appears. Ordinary native feedback and in-app messages retain their eligible cached path.

Triggered surveys can arrive with an authorized attempt, avoiding a separate start request before display. Eligible cached React Native surveys can also save their attempt locally before opening: these must be repeatable, uncapped and non-personalized, with an unexpired server-issued start permission. See survey delivery and recovery for offline boundaries. This optimization does not promise instant offline completion or new personalized messages while offline.

Test your integration

Use one known demo user. First preview a saved-name greeting, then record watching one show and preview its title and typed JSON destination. Watch a second show and check that a new latest-activity message changes. Send a different show in a triggering event and check that message uses the triggering show's values.

Unset the name to exercise its fallback. Try a user with no watch history to confirm a required movie ID skips delivery. Finally, tap the actual action and verify the app opens the expected show and position. For push, a successful preview is separate from delivery: complete the push setup and device test.