Recommendations × AI Agents = Automated Revenue Maximization
In app monetization, showing "the right offer to the right user at the right time" is both the most important and most challenging problem. Traditional rule-based approaches (showing everyone the same ad, sending a discount after 7 days) can't adapt to diverse user behavior patterns.
AI agents change this equation. They analyze user behavior data in real time and automatically generate individualized billing offers. This article details the design and implementation of a personalized recommendation revenue engine for Rork apps.
How the Personalized Revenue Engine Works
Three Core Components
- Behavior Analysis Engine — Tracks and scores in-app user behavior
- Recommendation AI Agent — Generates optimal offers based on behavior data
- Billing Execution Layer — Integrates with Stripe to present offers and process payments
Data Flow
User behavior → Analysis → AI agent decision → Recommendation displayed
↑ ↓
└────────── Result feedback ←──── Purchase/dismiss outcome
Implementing the Behavior Analysis Engine
// services/behavior-tracker.ts
// User behavior analysis engine
interface UserBehavior {
userId: string;
sessionCount: number;
avgSessionDuration: number; // seconds
featuresUsed: string[];
lastActiveAt: Date;
purchaseHistory: {
amount: number;
date: Date;
product: string;
}[];
premiumFeatureAttempts: number; // premium feature tap count
referralCount: number;
}
interface EngagementScore {
overall: number; // 0-100
purchaseIntent: number; // 0-100
churnRisk: number; // 0-100
optimalTiming: string; // best moment to show recommendation
}
function calculateEngagementScore(
behavior: UserBehavior
): EngagementScore {
// Calculate engagement score
const sessionScore = Math.min(behavior.sessionCount * 2, 30);
const durationScore = Math.min(behavior.avgSessionDuration / 60 * 5, 20);
const featureScore = Math.min(behavior.featuresUsed.length * 3, 20);
const premiumScore = Math.min(behavior.premiumFeatureAttempts * 10, 30);
const overall = sessionScore + durationScore + featureScore + premiumScore;
// Purchase intent (higher with more premium feature taps)
const purchaseIntent = Math.min(
behavior.premiumFeatureAttempts * 15 +
(behavior.sessionCount > 10 ? 20 : 0) +
(behavior.avgSessionDuration > 300 ? 15 : 0),
100
);
// Churn risk (based on days since last activity)
const daysSinceActive = Math.floor(
(Date.now() - behavior.lastActiveAt.getTime()) / 86400000
);
const churnRisk = Math.min(daysSinceActive * 10, 100);
// Optimal timing
let optimalTiming = "session_end"; // default
if (purchaseIntent > 70) {
optimalTiming = "premium_feature_tap"; // high intent → offer immediately
} else if (purchaseIntent > 40) {
optimalTiming = "session_mid"; // moderate → mid-session
}
return { overall, purchaseIntent, churnRisk, optimalTiming };
}
// Usage example
const score = calculateEngagementScore({
userId: "user_123",
sessionCount: 15,
avgSessionDuration: 420,
featuresUsed: ["search", "favorites", "share", "compare"],
lastActiveAt: new Date(),
purchaseHistory: [],
premiumFeatureAttempts: 5,
referralCount: 2
});
console.log(score);
// Expected output:
// {
// overall: 82,
// purchaseIntent: 75,
// churnRisk: 0,
// optimalTiming: "premium_feature_tap"
// }AI Agent Recommendation Generation
// services/recommendation-agent.ts
// AI agent for personalized recommendations
import { GoogleGenerativeAI } from "@google/generative-ai";
const genAI = new GoogleGenerativeAI(process.env.GEMINI_API_KEY!);
const model = genAI.getGenerativeModel({ model: "gemini-2.5-pro" });
interface Recommendation {
type: "subscription" | "one_time" | "upgrade" | "retention";
plan: string;
message_ja: string;
message_en: string;
discount?: number;
urgency: "low" | "medium" | "high";
displayTiming: string;
}
async function generateRecommendation(
behavior: UserBehavior,
score: EngagementScore
): Promise<Recommendation> {
const prompt = `You are a mobile app revenue optimization agent.
Generate the optimal billing recommendation based on this user data.
User data:
- Sessions: ${behavior.sessionCount}
- Avg session duration: ${behavior.avgSessionDuration}s
- Features used: ${behavior.featuresUsed.join(", ")}
- Premium feature taps: ${behavior.premiumFeatureAttempts}
- Purchase history: ${behavior.purchaseHistory.length} items
- Referrals: ${behavior.referralCount}
Scores:
- Engagement: ${score.overall}/100
- Purchase intent: ${score.purchaseIntent}/100
- Churn risk: ${score.churnRisk}/100
- Optimal timing: ${score.optimalTiming}
Available plans:
- Tip: $1.50 (one-time)
- Pro monthly: $3
- Premium lifetime: $10
Respond in this JSON format:
{
"type": "subscription" | "one_time" | "upgrade" | "retention",
"plan": "plan name",
"message_ja": "Japanese recommendation (under 50 chars)",
"message_en": "English recommendation (under 50 words)",
"discount": 0-30,
"urgency": "low" | "medium" | "high",
"displayTiming": "when to show"
}`;
const response = await model.generateContent(prompt);
const text = response.response.text();
const jsonMatch = text.match(/\{[\s\S]*\}/);
if (!jsonMatch) throw new Error("Failed to parse recommendation");
return JSON.parse(jsonMatch[0]);
}
// Expected output:
// {
// "type": "subscription",
// "plan": "Pro monthly",
// "message_en": "Unlock unlimited favorites with Pro — 20% off today",
// "message_ja": "お気に入り機能を無制限で使えるProプラン、今なら20%オフ",
// "discount": 20,
// "urgency": "high",
// "displayTiming": "premium_feature_tap"
// }Displaying Recommendations in the Rork App
// components/SmartOffer.tsx
// AI-generated recommendation display component
import React, { useEffect, useState } from 'react';
import {
View, Text, TouchableOpacity,
Animated, StyleSheet
} from 'react-native';
interface OfferProps {
userId: string;
currentScreen: string;
trigger: "session_end" | "premium_feature_tap" | "session_mid";
}
export default function SmartOffer({
userId, currentScreen, trigger
}: OfferProps) {
const [offer, setOffer] = useState<any>(null);
const [visible, setVisible] = useState(false);
const fadeAnim = useState(new Animated.Value(0))[0];
useEffect(() => {
fetchOffer();
}, [trigger]);
const fetchOffer = async () => {
try {
const res = await fetch(
'https://api.your-app.com/recommendations',
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer TOKEN'
},
body: JSON.stringify({ userId, currentScreen, trigger }),
}
);
const data = await res.json();
if (data.recommendation) {
setOffer(data.recommendation);
setVisible(true);
Animated.timing(fadeAnim, {
toValue: 1,
duration: 300,
useNativeDriver: true,
}).start();
}
} catch (e) {
// Silent fail — don't disrupt UX for recommendation errors
}
};
if (!visible || !offer) return null;
return (
<Animated.View style={[styles.container, { opacity: fadeAnim }]}>
<Text style={styles.title}>{offer.message_en}</Text>
{offer.discount > 0 && (
<Text style={styles.discount}>{offer.discount}% OFF</Text>
)}
<TouchableOpacity
style={styles.button}
onPress={() => {
// Navigate to Stripe Checkout
// handlePurchase(offer.plan, offer.discount);
}}
>
<Text style={styles.buttonText}>Learn More</Text>
</TouchableOpacity>
<TouchableOpacity onPress={() => setVisible(false)}>
<Text style={styles.dismiss}>Maybe Later</Text>
</TouchableOpacity>
</Animated.View>
);
}
const styles = StyleSheet.create({
container: {
position: 'absolute', bottom: 20, left: 16, right: 16,
backgroundColor: '#FFF', borderRadius: 16, padding: 20,
shadowColor: '#000', shadowOpacity: 0.15, shadowRadius: 10,
elevation: 5,
},
title: { fontSize: 16, fontWeight: '600', marginBottom: 8 },
discount: {
fontSize: 24, fontWeight: 'bold', color: '#E91E63',
marginBottom: 12
},
button: {
backgroundColor: '#2196F3', padding: 14, borderRadius: 10,
alignItems: 'center',
},
buttonText: { color: '#FFF', fontSize: 16, fontWeight: '600' },
dismiss: {
textAlign: 'center', color: '#999', marginTop: 12, fontSize: 14,
},
});Measurement and Feedback Loops
To continuously improve recommendations, feed outcome data back to the AI agent. Track click-through rates, purchase rates, and dismissal rates for every offer. Include this history in the next recommendation prompt, allowing the agent to learn patterns like "this user type responds better to feature highlights than discounts."
Wrapping Up — Revenue Without the Hard Sell
The essence of AI-powered recommendation monetization is maximizing revenue without degrading user experience. Presenting truly valuable offers at exactly the moment users need them — this "no hard sell" approach is the foundation of a sustainable business model.
Start with simple behavior scoring, then gradually introduce AI agent personalization as your data grows.
For more on app monetization, check out our Rork Subscription Revenue Model Design and Rork Monetization Masterplan.