A Developer's Guide to Amply's Event-Based Targeting and Triggering
02 Mar 2025
Modern mobile apps need more than feature flags and static configurations. They need campaign orchestration — the ability to show the right message to the right user at precisely the right moment. Amply's SDK gives you a powerful rules engine that evaluates targeting and triggering conditions on-device, using JsonLogic expressions against real-time datasets. No server round-trips. No latency. Just instant, context-aware campaigns.
This guide walks you through every targeting and triggering feature available in the Amply SDK. By the end, you'll understand how to compose rules that select your audience (targeting), choose the perfect moment (triggering), and control frequency so campaigns feel helpful rather than intrusive.
Understanding Targeting: Selecting Your Audience
Targeting answers one question: who should see this campaign? Amply evaluates targeting rules before any triggering logic runs. If a user does not match the targeting criteria, the campaign is skipped entirely.
Device Property Targeting
The @device dataset exposes properties the SDK collects automatically: app version, OS version, device country, install timestamp, and current server-synced time:
// Target users on app version 1.x, excluding US and Poland
{
"and": [
{ ">": [{ "var": "@device.appVersionNormalized" }, 100000000] },
{ "<": [{ "var": "@device.appVersionNormalized" }, 200000000] },
{ "!": { "in": [{ "var": "@device.country" }, ["PL", "US"]] } }
]
}Custom Property Targeting
Custom properties let you target based on values your app sets explicitly via the @custom dataset:
// Set custom properties in your app
AmplySDK.setCustomProperties(mapOf(
"is_premium" to false,
"user_level" to 5,
"preferred_language" to "en"
))// Target non-premium users above level 3
{
"and": [
{ "==": [{ "var": "@custom.is_premium" }, false] },
{ ">": [{ "var": "@custom.user_level" }, 3] }
]
}Event-Based Targeting (Has-Event)
The most powerful targeting method uses the @events dataset to check a user's event history with optional count thresholds and parameter matching:
// Target users who have completed fewer than 3 purchases
{
"some": [
{ "var": "@events.data" },
{ "and": [
{ "==": [{ "var": "name" }, "Purchase"] },
{ "==": [{ "var": "type" }, "custom"] },
{ "<": [{ "var": "counter" }, 3] }
]}
]
}Understanding Triggering: Choosing the Right Moment
While targeting filters who sees a campaign, triggering controls when it fires. Triggering rules evaluate every time your app tracks an event.
Event Matching
// Trigger on a custom "LevelComplete" event
{
"and": [
{ "==": [{ "var": "@triggeredEvent.name" }, "LevelComplete"] },
{ "==": [{ "var": "@triggeredEvent.type" }, "custom"] }
]
}AmplySDK.track("LevelComplete", mapOf(
"level" to 5,
"score" to 2400
))Repetition Patterns
The EVERY pattern uses modulo to fire on every Nth occurrence:
// Show campaign on every 3rd session
{
"and": [
{ "==": [{ "var": "@triggeredEvent.name" }, "SessionStarted"] },
{ "==": [{ "%": [{ "var": "@triggeredEvent.counter" }, 3] }, 0] }
]
}
// dataSetContexts: { "@triggeredEvent": { "countStrategy": "global" } }When using the EVERY pattern with session events, always use countStrategy "global". A "session" strategy counts within the current session only — and since SessionStarted fires once per session, the counter is always 1.
The INTERVAL pattern uses range comparisons to fire within a specific window:
// Show campaign between sessions 3 and 7
{
"and": [
{ ">=": [{ "var": "@session.counter" }, 3] },
{ "<=": [{ "var": "@session.counter" }, 7] }
]
}Count Strategies: Global vs. Session
- GLOBAL: Lifetime total across all sessions. Use for "every Nth session" or "after N total purchases."
- SESSION: Count within current session only. Use for "every 5th item viewed this session."
Controlling Frequency: Limits and Intervals
Impression Limits
The SDK tracks impressions as system events (Campaign Shown). A targeting rule checks the event count against a maximum:
// Limit to 3 total impressions (lifetime)
{
"some": [
{ "var": "@events.data" },
{ "and": [
{ "==": [{ "var": "name" }, "Campaign Shown"] },
{ "some": [{ "var": "params" }, { "and": [
{ "==": [{ "var": "name" }, "campaignId"] },
{ "==": [{ "var": "value" }, "your-campaign-id"] }
]}]},
{ "<": [{ "var": "counter" }, 3] }
]}
]
}Time Intervals Between Impressions
// Minimum 24 hours between impressions
{
">=": [
{ "-": [
{ "var": "@device.nowTs" },
{ "var": "@triggeredEvent.lastTriggeredTs" }
]},
86400000
]
}Timestamps are in milliseconds: 60000 for one minute, 3600000 for one hour, 86400000 for one day. Both timestamps are server-synced, preventing clock manipulation.
Actions: What Happens When a Campaign Fires
DeepLink
// Handle deeplinks in your app
AmplySDK.setDeeplinkHandler { url ->
when {
url.contains("/promo/") -> navigateToPromoScreen(url)
url.contains("/feature/") -> navigateToFeature(url)
else -> openInBrowser(url)
}
}RateReview
Triggers the native in-app review dialog provided by the App Store or Google Play. The SDK invokes the platform's native review API automatically — no additional handling needed.
Putting It All Together: A Complete Example
Let's build a real-world campaign: a promotional deeplink shown to non-premium users on every 3rd session, only on app version 1.x, with a max of 5 lifetime impressions.
// Initialize the SDK
AmplySDK.initialize(context, "YOUR_API_KEY")
// Set custom properties
AmplySDK.setCustomProperties(mapOf(
"is_premium" to false,
"user_level" to 12
))
// Track purchases with parameters
AmplySDK.track("Purchase", mapOf(
"amount" to 99.99,
"currency" to "USD",
"item" to "premium_monthly"
))
// Handle deeplinks from campaigns
AmplySDK.setDeeplinkHandler { url ->
handleDeeplink(url)
}This single campaign combines device targeting (version range, country exclusion), custom property targeting (non-premium users), impression limits (max 5 via @events), and session-based triggering (every 3rd session via modulo). All evaluation happens on-device with zero latency.
Best Practices
- Start with targeting, then add triggering. Get your audience segment right first.
- Use GLOBAL count strategy for session-based patterns. The session-scoped counter for
SessionStartedis always 1. - Always set impression limits. Even well-targeted campaigns become annoying without a cap.
- Add time intervals for sensitive actions. Rate review prompts should have at least 24 hours (86400000ms) between impressions.
- Combine event parameters for precision. Filter by currency, item type, or amount to reach the right behavioral segment.
- Test with debug logging. The Amply SDK logs detailed campaign evaluation traces at the DEBUG level.
Conclusion
Amply's event-based targeting and triggering system gives you a complete toolkit for in-app campaign orchestration. Device properties, custom attributes, and historical event data let you define precise audience segments. Event matching, repetition patterns, and count strategies let you pick the perfect moment. Impression limits and time intervals keep the experience respectful.
Every rule evaluates on-device using JsonLogic against real-time datasets — no network calls, no delays. Start simple with a single targeting rule and one trigger. Layer on frequency controls as you learn what resonates. Your users will thank you for campaigns that feel timely and relevant rather than random and repetitive.