RoxyAPI

Menu

Building Daily Numerology Insights with Personal Year API Calls

20 min read
By Sofia Rodriguez
NumerologyPersonal YearDaily InsightsWellness AppsNotifications

Create engaging daily numerology features with Personal Year forecasts. Learn to build yearly cycle tracking, personalized insights, and notification systems.

Building Daily Numerology Insights with Personal Year API Calls

Daily insights and forecasts are one of the most engaging features you can add to wellness, astrology, or spiritual guidance apps. While daily horoscopes dominate the market, numerology's Personal Year cycle offers a unique alternative that provides consistent, meaningful guidance throughout the year.

In this comprehensive tutorial, you will learn how to build daily numerology insights using the Personal Year endpoint from RoxyAPI's Numerology API. We will cover yearly cycle tracking, personalized daily messages, notification systems, and user engagement strategies.

Understanding the 9-Year Personal Year Cycle

Before building the feature, it is essential to understand what Personal Year means in numerology.

What is a Personal Year?

Every person moves through a predictable 9-year cycle, with each year carrying specific energy and themes. Your Personal Year number (1-9) reveals the universal influences affecting your life for the current year.

The cycle follows this pattern:

  1. Year 1 - New Beginnings: Start projects, take initiative, focus on self
  2. Year 2 - Cooperation: Build relationships, practice patience, wait for growth
  3. Year 3 - Creativity: Express yourself, socialize, enjoy creative projects
  4. Year 4 - Foundation: Build structure, work hard, establish stability
  5. Year 5 - Change: Embrace freedom, travel, adapt to unexpected shifts
  6. Year 6 - Responsibility: Focus on family, home, and service to others
  7. Year 7 - Introspection: Study, meditate, do inner spiritual work
  8. Year 8 - Achievement: Gain recognition, financial rewards, power
  9. Year 9 - Completion: Release what no longer serves, finish projects, prepare for new cycle

How Personal Year is Calculated

The calculation uses your birth month, birth day, and the current year:

Personal Year = (Birth Month + Birth Day + Current Year) reduced to 1-9

Example: Someone born May 15 in year 2026:

5 (May) + 15 (day) + 2026 (current year) = 2046
2 + 0 + 4 + 6 = 12
1 + 2 = 3
Personal Year = 3

Important: Personal Years are ALWAYS reduced to single digits (1-9). Unlike Life Path numbers, master numbers (11, 22, 33) are further reduced because Personal Year operates on simpler, more practical energies.

What We Will Build

In this tutorial, you will build:

  1. Personal Year Calculator - Determine which year a user is currently in
  2. Daily Insight Generator - Create personalized daily messages based on yearly themes
  3. Yearly Forecast Display - Show detailed annual guidance
  4. Push Notification System - Send daily insights automatically
  5. Cycle Progress Tracker - Visual representation of the 9-year journey

All features will use the RoxyAPI Numerology API Personal Year endpoint.

Prerequisites

Before starting, ensure you have:

  • Node.js 18+ or Bun runtime
  • RoxyAPI account with API key (sign up here)
  • React Native or Next.js project
  • User profiles with birth dates stored

Step 1: Setting Up the Personal Year API Client

Create a reusable client for Personal Year calculations:

// lib/personal-year-client.ts
const NUMEROLOGY_API = 'https://roxyapi.com/api/v2/numerology';
const API_KEY = process.env.ROXYAPI_KEY!;

export interface PersonalYearResponse {
  personalYear: number; // 1-9
  cycle: string; // "Year 5 of 9"
  theme: string; // "Freedom and Change"
  forecast: string; // Detailed 200-300 word forecast
  opportunities: string[]; // Key opportunities this year
  challenges: string[]; // Challenges to navigate
  advice: string; // Practical guidance
}

export async function calculatePersonalYear(
  month: number,
  day: number,
  year?: number
): Promise<PersonalYearResponse> {
  const response = await fetch(`${NUMEROLOGY_API}/personal-year`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'X-API-Key': API_KEY,
    },
    body: JSON.stringify({
      month,
      day,
      year: year || new Date().getFullYear(),
    }),
  });

  if (!response.ok) {
    throw new Error(`Personal Year API error: ${response.statusText}`);
  }

  return response.json();
}

export async function getPersonalYearForUser(userId: string): Promise<PersonalYearResponse> {
  const user = await prisma.user.findUnique({
    where: { id: userId },
    select: { birthDate: true },
  });

  if (!user?.birthDate) {
    throw new Error('User birth date not found');
  }

  const birthDate = new Date(user.birthDate);
  const month = birthDate.getMonth() + 1; // JavaScript months are 0-indexed
  const day = birthDate.getDate();

  return calculatePersonalYear(month, day);
}

Step 2: Storing and Caching Personal Year Data

Since Personal Year only changes once per year, cache it aggressively:

// lib/personal-year-cache.ts
import { Redis } from '@upstash/redis';

const redis = new Redis({
  url: process.env.UPSTASH_REDIS_URL!,
  token: process.env.UPSTASH_REDIS_TOKEN!,
});

export async function getCachedPersonalYear(
  userId: string
): Promise<PersonalYearResponse | null> {
  const key = `personal-year:${userId}:${new Date().getFullYear()}`;
  const cached = await redis.get<PersonalYearResponse>(key);
  return cached;
}

export async function cachePersonalYear(
  userId: string,
  data: PersonalYearResponse
): Promise<void> {
  const key = `personal-year:${userId}:${new Date().getFullYear()}`;
  const now = new Date();
  const endOfYear = new Date(now.getFullYear(), 11, 31, 23, 59, 59);
  const secondsUntilEndOfYear = Math.floor((endOfYear.getTime() - now.getTime()) / 1000);

  // Cache until end of current year
  await redis.setex(key, secondsUntilEndOfYear, JSON.stringify(data));
}

export async function getOrCalculatePersonalYear(
  userId: string
): Promise<PersonalYearResponse> {
  // Check cache first
  const cached = await getCachedPersonalYear(userId);
  if (cached) {
    return cached;
  }

  // Calculate and cache
  const personalYear = await getPersonalYearForUser(userId);
  await cachePersonalYear(userId, personalYear);

  return personalYear;
}

Store in database for analytics:

// prisma/schema.prisma (add to User model)
model User {
  id         String   @id @default(cuid())
  birthDate  DateTime
  // ... other fields
  
  personalYearCache PersonalYearCache?
}

model PersonalYearCache {
  id           String   @id @default(cuid())
  userId       String   @unique
  user         User     @relation(fields: [userId], references: [id])
  year         Int      // 2026
  personalYear Int      // 1-9
  theme        String
  forecast     String   @db.Text
  opportunities Json
  challenges    Json
  advice       String   @db.Text
  calculatedAt DateTime @default(now())
  
  @@index([userId, year])
}

Background job to pre-calculate for all users on January 1st:

// jobs/update-personal-years.ts
export async function updateAllPersonalYears() {
  const users = await prisma.user.findMany({
    where: { birthDate: { not: null } },
    select: { id: true, birthDate: true },
  });

  console.log(`Updating Personal Year for ${users.length} users...`);

  const results = await Promise.allSettled(
    users.map(async (user) => {
      const birthDate = new Date(user.birthDate!);
      const personalYear = await calculatePersonalYear(
        birthDate.getMonth() + 1,
        birthDate.getDate()
      );

      await prisma.personalYearCache.upsert({
        where: { userId: user.id },
        create: {
          userId: user.id,
          year: new Date().getFullYear(),
          personalYear: personalYear.personalYear,
          theme: personalYear.theme,
          forecast: personalYear.forecast,
          opportunities: personalYear.opportunities,
          challenges: personalYear.challenges,
          advice: personalYear.advice,
        },
        update: {
          year: new Date().getFullYear(),
          personalYear: personalYear.personalYear,
          theme: personalYear.theme,
          forecast: personalYear.forecast,
          opportunities: personalYear.opportunities,
          challenges: personalYear.challenges,
          advice: personalYear.advice,
          calculatedAt: new Date(),
        },
      });

      return { userId: user.id, success: true };
    })
  );

  const successful = results.filter((r) => r.status === 'fulfilled').length;
  const failed = results.filter((r) => r.status === 'rejected').length;

  console.log(`✓ Updated ${successful} users, ${failed} failures`);
}

Schedule with cron (runs January 1st at midnight):

// jobs/scheduler.ts
import cron from 'node-cron';
import { updateAllPersonalYears } from './update-personal-years';

// Run at midnight on January 1st every year
cron.schedule('0 0 1 1 *', async () => {
  console.log('Starting annual Personal Year update...');
  await updateAllPersonalYears();
});

Step 3: Building the Daily Insight Generator

Create a system that generates unique daily messages based on yearly themes:

// lib/daily-insights.ts
export interface DailyInsight {
  date: string; // "2026-01-09"
  personalYear: number;
  theme: string;
  insight: string; // 100-150 character daily message
  affirmation: string; // 50-80 character affirmation
  actionItem: string; // Practical daily action
  emoji: string; // Theme emoji
}

const DAILY_INSIGHTS_BY_YEAR: Record<
  number,
  { insights: string[]; affirmations: string[]; actions: string[]; emoji: string }
> = {
  1: {
    emoji: '🌱',
    insights: [
      'Today is perfect for starting something new. Take the first step on that project.',
      'Your independence shines today. Trust your instincts and lead with confidence.',
      'New opportunities are presenting themselves. Be bold and seize the moment.',
      'This is your time to focus on personal goals. Put yourself first today.',
      'Initiative you take today plants seeds for the entire year ahead.',
    ],
    affirmations: [
      'I am a leader and pioneer',
      'I embrace new beginnings fearlessly',
      'I trust my ability to create',
      'I am independent and capable',
      'My ideas are valuable and original',
    ],
    actions: [
      'Start a new habit or project',
      'Make a bold decision you have been postponing',
      'Write down your top 3 goals for this year',
      'Take initiative in a leadership opportunity',
      'Do something outside your comfort zone',
    ],
  },
  2: {
    emoji: '🤝',
    insights: [
      'Patience is your superpower today. Trust the process and allow things to unfold.',
      'Focus on cooperation over competition. Relationships deepen with genuine listening.',
      'Small acts of kindness create ripples. Be the supportive presence others need.',
      'Details matter today. Slow down and give attention to the little things.',
      'Your sensitivity is a strength. Use your intuition to navigate situations.',
    ],
    affirmations: [
      'I am patient and trust divine timing',
      'I create harmony in all my relationships',
      'I listen with compassion and understanding',
      'I attract supportive partnerships',
      'My gentle approach creates powerful results',
    ],
    actions: [
      'Reach out to someone you care about',
      'Practice active listening in conversations',
      'Collaborate on a project instead of working alone',
      'Write a thank-you note to someone who helped you',
      'Meditate for 10 minutes to cultivate patience',
    ],
  },
  3: {
    emoji: '🎨',
    insights: [
      'Your creativity is flowing freely today. Express yourself without holding back.',
      'Social connections bring joy and inspiration. Reach out and connect.',
      'Today favors communication. Share your ideas, write, speak, or create.',
      'Playfulness and fun are productive today. Approach tasks with lightheartedness.',
      'Your enthusiasm is contagious. Inspire others with your positive energy.',
    ],
    affirmations: [
      'I express myself authentically and joyfully',
      'My creativity knows no bounds',
      'I attract fun and inspiring connections',
      'I communicate my truth with confidence',
      'Joy and creativity flow through me',
    ],
    actions: [
      'Create something for pure enjoyment',
      'Call or text three friends today',
      'Share an idea you have been keeping to yourself',
      'Attend a social event or gathering',
      'Write in a journal about what brings you joy',
    ],
  },
  4: {
    emoji: '🏗️',
    insights: [
      'Consistency and hard work today build the foundation for future success.',
      'Focus on practical matters. Organization and planning bring clarity.',
      'Discipline yourself today and you will thank yourself tomorrow.',
      'Small, steady progress is more valuable than dramatic leaps right now.',
      'Your reliability is your greatest asset. Show up and do the work.',
    ],
    affirmations: [
      'I build strong foundations with consistent effort',
      'I am disciplined and organized',
      'Hard work brings me satisfaction and results',
      'I create stability in all areas of my life',
      'I am reliable and trustworthy',
    ],
    actions: [
      'Complete a task you have been avoiding',
      'Organize one area of your home or workspace',
      'Create a detailed plan for a project',
      'Review your budget or financial goals',
      'Establish a new productive routine',
    ],
  },
  5: {
    emoji: '✈️',
    insights: [
      'Embrace change and unexpected opportunities today. Flexibility is key.',
      'Adventure calls you. Step outside routine and try something new.',
      'Freedom is yours to claim. Release what restricts your growth.',
      'Variety adds spice to life today. Mix things up and break from monotony.',
      'Adaptability is your strength. Go with the flow and trust the journey.',
    ],
    affirmations: [
      'I embrace change with excitement and curiosity',
      'I am free to explore and grow',
      'I adapt easily to new situations',
      'Adventure and opportunity flow to me',
      'I trust life's unexpected turns',
    ],
    actions: [
      'Try something you have never done before',
      'Take a different route to work or home',
      'Say yes to an unexpected invitation',
      'Plan a trip or adventure, even a small one',
      'Let go of something that no longer serves you',
    ],
  },
  6: {
    emoji: '❤️',
    insights: [
      'Love and care are your gifts today. Nurture yourself and those around you.',
      'Family and home need your attention. Create beauty and harmony in your space.',
      'Responsibility brings fulfillment today. Serve others with an open heart.',
      'Balance giving and receiving. Allow others to support you too.',
      'Your compassion makes a real difference. Small acts of love create lasting impact.',
    ],
    affirmations: [
      'I give and receive love freely',
      'I create harmony in my relationships',
      'I nurture myself and others with care',
      'My home is a sanctuary of peace',
      'I am responsible and loving',
    ],
    actions: [
      'Cook a meal for someone you love',
      'Spend quality time with family',
      'Beautify your living space',
      'Volunteer or help someone in need',
      'Practice self-care and self-compassion',
    ],
  },
  7: {
    emoji: '🧘',
    insights: [
      'Solitude is productive today. Take time for introspection and inner work.',
      'Trust your intuition. The answers you seek are within you.',
      'Deep thinking and analysis bring clarity. Study, research, and contemplate.',
      'Spiritual practices nourish you today. Meditate, pray, or connect with nature.',
      'Quality over quantity in all things. Choose depth over superficiality.',
    ],
    affirmations: [
      'I trust my inner wisdom',
      'I find peace in solitude',
      'My intuition guides me perfectly',
      'I seek truth and deeper understanding',
      'I am spiritually connected',
    ],
    actions: [
      'Meditate for at least 15 minutes',
      'Spend time alone in nature',
      'Read something that expands your mind',
      'Journal about your spiritual journey',
      'Turn off devices and practice digital detox',
    ],
  },
  8: {
    emoji: '💼',
    insights: [
      'Your efforts are being recognized. Step into your power with confidence.',
      'Financial opportunities are present. Make strategic decisions today.',
      'Leadership and authority come naturally now. Own your success.',
      'Think big and aim high. Your ambitions are achievable.',
      'Balance material success with spiritual values for true fulfillment.',
    ],
    affirmations: [
      'I am powerful and successful',
      'Abundance flows to me easily',
      'I make wise financial decisions',
      'I am worthy of recognition and reward',
      'I balance power with integrity',
    ],
    actions: [
      'Negotiate for what you deserve',
      'Review and optimize your finances',
      'Take on a leadership role or responsibility',
      'Network with influential people',
      'Invest in yourself or your future',
    ],
  },
  9: {
    emoji: '🌅',
    insights: [
      'Completion brings peace. Finish what you started and release what is complete.',
      'Forgiveness frees you. Let go of old wounds and make space for new beginnings.',
      'Your wisdom and compassion are needed. Share what you have learned.',
      'Endings are not failures but natural cycles. Trust the process of closure.',
      'Prepare for rebirth. As you close this chapter, a new one awaits.',
    ],
    affirmations: [
      'I release with love and gratitude',
      'Endings are new beginnings in disguise',
      'I forgive and let go',
      'I share my wisdom with compassion',
      'I trust the cycles of life',
    ],
    actions: [
      'Complete a project you have been dragging on',
      'Declutter and donate items you no longer need',
      'Forgive someone (including yourself)',
      'Reflect on lessons learned this year',
      'Express gratitude for what is ending',
    ],
  },
};

export function getDailyInsight(personalYear: number, date: Date): DailyInsight {
  const yearData = DAILY_INSIGHTS_BY_YEAR[personalYear];
  const dayOfYear = Math.floor(
    (date.getTime() - new Date(date.getFullYear(), 0, 0).getTime()) / 86400000
  );

  // Rotate through insights based on day of year
  const insightIndex = dayOfYear % yearData.insights.length;
  const affirmationIndex = dayOfYear % yearData.affirmations.length;
  const actionIndex = dayOfYear % yearData.actions.length;

  return {
    date: date.toISOString().split('T')[0],
    personalYear,
    theme: `Personal Year ${personalYear}`,
    insight: yearData.insights[insightIndex],
    affirmation: yearData.affirmations[affirmationIndex],
    actionItem: yearData.actions[actionIndex],
    emoji: yearData.emoji,
  };
}

export async function getTodaysInsight(userId: string): Promise<DailyInsight> {
  const personalYearData = await getOrCalculatePersonalYear(userId);
  const today = new Date();

  return getDailyInsight(personalYearData.personalYear, today);
}

Step 4: Building the UI Components

Daily Insight Card (React Native)

// components/DailyInsightCard.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet, ActivityIndicator } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';

export function DailyInsightCard({ userId }: { userId: string }) {
  const [insight, setInsight] = useState<DailyInsight | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchInsight() {
      try {
        const response = await fetch(`/api/users/${userId}/daily-insight`);
        const data = await response.json();
        setInsight(data);
      } catch (error) {
        console.error('Failed to fetch daily insight:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchInsight();
  }, [userId]);

  if (loading) {
    return (
      <View style={styles.loadingContainer}>
        <ActivityIndicator size="large" color="#6B46C1" />
      </View>
    );
  }

  if (!insight) return null;

  const gradients: Record<number, string[]> = {
    1: ['#FF6B6B', '#FF8E53'],
    2: ['#4ECDC4', '#44A08D'],
    3: ['#F7B731', '#FC7C54'],
    4: ['#8B5CF6', '#6366F1'],
    5: ['#10B981', '#059669'],
    6: ['#EC4899', '#DB2777'],
    7: ['#3B82F6', '#1D4ED8'],
    8: ['#F59E0B', '#D97706'],
    9: ['#6366F1', '#4F46E5'],
  };

  return (
    <LinearGradient
      colors={gradients[insight.personalYear]}
      start={{ x: 0, y: 0 }}
      end={{ x: 1, y: 1 }}
      style={styles.card}
    >
      <View style={styles.header}>
        <Text style={styles.emoji}>{insight.emoji}</Text>
        <Text style={styles.theme}>{insight.theme}</Text>
      </View>

      <Text style={styles.insight}>{insight.insight}</Text>

      <View style={styles.affirmationContainer}>
        <Text style={styles.affirmationLabel}>Today's Affirmation</Text>
        <Text style={styles.affirmation}>"{insight.affirmation}"</Text>
      </View>

      <View style={styles.actionContainer}>
        <Text style={styles.actionLabel}>🎯 Action of the Day</Text>
        <Text style={styles.action}>{insight.actionItem}</Text>
      </View>
    </LinearGradient>
  );
}

const styles = StyleSheet.create({
  loadingContainer: {
    padding: 40,
    alignItems: 'center',
  },
  card: {
    borderRadius: 20,
    padding: 24,
    marginHorizontal: 16,
    marginVertical: 12,
    shadowColor: '#000',
    shadowOffset: { width: 0, height: 4 },
    shadowOpacity: 0.3,
    shadowRadius: 12,
    elevation: 8,
  },
  header: {
    flexDirection: 'row',
    alignItems: 'center',
    marginBottom: 16,
  },
  emoji: {
    fontSize: 36,
    marginRight: 12,
  },
  theme: {
    fontSize: 18,
    fontWeight: '600',
    color: '#FFFFFF',
  },
  insight: {
    fontSize: 16,
    color: '#FFFFFF',
    lineHeight: 24,
    marginBottom: 20,
  },
  affirmationContainer: {
    backgroundColor: 'rgba(255, 255, 255, 0.2)',
    borderRadius: 12,
    padding: 16,
    marginBottom: 16,
  },
  affirmationLabel: {
    fontSize: 12,
    fontWeight: '600',
    color: '#FFFFFF',
    opacity: 0.9,
    marginBottom: 6,
    textTransform: 'uppercase',
    letterSpacing: 1,
  },
  affirmation: {
    fontSize: 15,
    fontStyle: 'italic',
    color: '#FFFFFF',
    lineHeight: 22,
  },
  actionContainer: {
    backgroundColor: 'rgba(255, 255, 255, 0.2)',
    borderRadius: 12,
    padding: 16,
  },
  actionLabel: {
    fontSize: 12,
    fontWeight: '600',
    color: '#FFFFFF',
    opacity: 0.9,
    marginBottom: 6,
    textTransform: 'uppercase',
    letterSpacing: 1,
  },
  action: {
    fontSize: 14,
    color: '#FFFFFF',
    lineHeight: 20,
  },
});

Personal Year Overview

// components/PersonalYearOverview.tsx
import React, { useEffect, useState } from 'react';
import { View, Text, ScrollView, StyleSheet } from 'react-native';

export function PersonalYearOverview({ userId }: { userId: string }) {
  const [yearData, setYearData] = useState<PersonalYearResponse | null>(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    async function fetchYearData() {
      try {
        const response = await fetch(`/api/users/${userId}/personal-year`);
        const data = await response.json();
        setYearData(data);
      } catch (error) {
        console.error('Failed to fetch Personal Year:', error);
      } finally {
        setLoading(false);
      }
    }

    fetchYearData();
  }, [userId]);

  if (loading || !yearData) return null;

  return (
    <ScrollView style={styles.container}>
      <View style={styles.headerCard}>
        <Text style={styles.cycle}>{yearData.cycle}</Text>
        <Text style={styles.yearNumber}>{yearData.personalYear}</Text>
        <Text style={styles.theme}>{yearData.theme}</Text>
      </View>

      <View style={styles.section}>
        <Text style={styles.sectionTitle}>Your Year Ahead</Text>
        <Text style={styles.forecast}>{yearData.forecast}</Text>
      </View>

      <View style={styles.section}>
        <Text style={styles.sectionTitle}>✨ Key Opportunities</Text>
        {yearData.opportunities.map((opportunity, index) => (
          <View key={index} style={styles.listItem}>
            <Text style={styles.bullet}>•</Text>
            <Text style={styles.listText}>{opportunity}</Text>
          </View>
        ))}
      </View>

      <View style={styles.section}>
        <Text style={styles.sectionTitle}>⚠️ Challenges to Navigate</Text>
        {yearData.challenges.map((challenge, index) => (
          <View key={index} style={styles.listItem}>
            <Text style={styles.bullet}>•</Text>
            <Text style={styles.listText}>{challenge}</Text>
          </View>
        ))}
      </View>

      <View style={styles.adviceCard}>
        <Text style={styles.adviceTitle}>💡 Guidance for This Year</Text>
        <Text style={styles.advice}>{yearData.advice}</Text>
      </View>
    </ScrollView>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F9FAFB',
  },
  headerCard: {
    backgroundColor: '#6B46C1',
    borderRadius: 20,
    padding: 32,
    marginHorizontal: 16,
    marginVertical: 20,
    alignItems: 'center',
  },
  cycle: {
    fontSize: 14,
    color: '#E9D5FF',
    fontWeight: '600',
    textTransform: 'uppercase',
    letterSpacing: 1,
    marginBottom: 8,
  },
  yearNumber: {
    fontSize: 72,
    fontWeight: 'bold',
    color: '#FFFFFF',
    marginBottom: 8,
  },
  theme: {
    fontSize: 20,
    color: '#FFFFFF',
    fontWeight: '600',
    textAlign: 'center',
  },
  section: {
    backgroundColor: '#FFFFFF',
    borderRadius: 16,
    padding: 20,
    marginHorizontal: 16,
    marginBottom: 16,
  },
  sectionTitle: {
    fontSize: 18,
    fontWeight: '600',
    color: '#111827',
    marginBottom: 12,
  },
  forecast: {
    fontSize: 15,
    color: '#4B5563',
    lineHeight: 24,
  },
  listItem: {
    flexDirection: 'row',
    marginBottom: 10,
  },
  bullet: {
    fontSize: 16,
    color: '#6B46C1',
    marginRight: 10,
    fontWeight: 'bold',
  },
  listText: {
    flex: 1,
    fontSize: 14,
    color: '#4B5563',
    lineHeight: 22,
  },
  adviceCard: {
    backgroundColor: '#FEF3C7',
    borderRadius: 16,
    padding: 20,
    marginHorizontal: 16,
    marginBottom: 20,
  },
  adviceTitle: {
    fontSize: 16,
    fontWeight: '600',
    color: '#92400E',
    marginBottom: 10,
  },
  advice: {
    fontSize: 14,
    color: '#78350F',
    lineHeight: 22,
  },
});

Step 5: Push Notification System

Send daily insights via push notifications:

// lib/send-daily-insights.ts
import * as Notifications from 'expo-notifications';

export async function scheduleDailyInsightNotifications(userId: string) {
  const personalYearData = await getOrCalculatePersonalYear(userId);
  const today = new Date();
  const insight = getDailyInsight(personalYearData.personalYear, today);

  // Schedule for 9 AM daily
  await Notifications.scheduleNotificationAsync({
    content: {
      title: `${insight.emoji} Your Personal Year ${insight.personalYear} Insight`,
      body: insight.insight,
      data: { userId, personalYear: insight.personalYear },
    },
    trigger: {
      hour: 9,
      minute: 0,
      repeats: true,
    },
  });
}

// Background job to send insights to all users
export async function sendDailyInsightsToAll() {
  const users = await prisma.user.findMany({
    where: {
      birthDate: { not: null },
      notificationsEnabled: true,
    },
    select: { id: true, pushToken: true },
  });

  const messages = await Promise.all(
    users.map(async (user) => {
      const personalYearData = await getOrCalculatePersonalYear(user.id);
      const today = new Date();
      const insight = getDailyInsight(personalYearData.personalYear, today);

      return {
        to: user.pushToken,
        title: `${insight.emoji} Personal Year ${insight.personalYear}`,
        body: insight.insight,
        data: { userId: user.id },
      };
    })
  );

  // Send in batches using Expo Push API
  await sendPushNotificationsBatch(messages);

  console.log(`✓ Sent daily insights to ${messages.length} users`);
}

Schedule with cron (runs daily at 9 AM):

// jobs/scheduler.ts
import cron from 'node-cron';
import { sendDailyInsightsToAll } from '../lib/send-daily-insights';

// Run daily at 9:00 AM
cron.schedule('0 9 * * *', async () => {
  console.log('Sending daily numerology insights...');
  await sendDailyInsightsToAll();
});

Step 6: Cycle Progress Visualization

Show users where they are in the 9-year cycle:

// components/CycleProgressRing.tsx
import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import Svg, { Circle } from 'react-native-svg';

export function CycleProgressRing({ personalYear }: { personalYear: number }) {
  const size = 200;
  const strokeWidth = 20;
  const center = size / 2;
  const radius = size / 2 - strokeWidth / 2;
  const circumference = 2 * Math.PI * radius;
  const progress = personalYear / 9;
  const strokeDashoffset = circumference - progress * circumference;

  const yearColors: Record<number, string> = {
    1: '#FF6B6B',
    2: '#4ECDC4',
    3: '#F7B731',
    4: '#8B5CF6',
    5: '#10B981',
    6: '#EC4899',
    7: '#3B82F6',
    8: '#F59E0B',
    9: '#6366F1',
  };

  return (
    <View style={styles.container}>
      <Svg width={size} height={size}>
        {/* Background circle */}
        <Circle
          cx={center}
          cy={center}
          r={radius}
          stroke="#E5E7EB"
          strokeWidth={strokeWidth}
          fill="none"
        />
        {/* Progress circle */}
        <Circle
          cx={center}
          cy={center}
          r={radius}
          stroke={yearColors[personalYear]}
          strokeWidth={strokeWidth}
          strokeDasharray={circumference}
          strokeDashoffset={strokeDashoffset}
          strokeLinecap="round"
          fill="none"
          transform={`rotate(-90 ${center} ${center})`}
        />
      </Svg>
      <View style={styles.textContainer}>
        <Text style={[styles.yearNumber, { color: yearColors[personalYear] }]}>
          {personalYear}
        </Text>
        <Text style={styles.label}>of 9</Text>
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    alignItems: 'center',
    justifyContent: 'center',
    position: 'relative',
  },
  textContainer: {
    position: 'absolute',
    alignItems: 'center',
  },
  yearNumber: {
    fontSize: 60,
    fontWeight: 'bold',
  },
  label: {
    fontSize: 18,
    color: '#6B7280',
    marginTop: -8,
  },
});

Engagement Strategies

1. Streak Tracking

Encourage daily check-ins:

// Track consecutive days user viewed insight
export async function trackInsightView(userId: string) {
  const today = new Date().toISOString().split('T')[0];

  await prisma.insightView.create({
    data: { userId, date: today },
  });

  const streak = await calculateCurrentStreak(userId);
  return { streak };
}

2. Yearly Milestones

Celebrate transitions between years:

export async function checkYearTransition(userId: string) {
  const lastYear = await getPersonalYearForDate(userId, new Date(Date.now() - 365 * 86400000));
  const currentYear = await getOrCalculatePersonalYear(userId);

  if (lastYear.personalYear !== currentYear.personalYear) {
    // Trigger celebration animation and notification
    await sendYearTransitionNotification(userId, currentYear.personalYear);
  }
}

3. Weekly Summary

Send weekly roundup emails:

export async function sendWeeklySummary(userId: string) {
  const personalYearData = await getOrCalculatePersonalYear(userId);
  const weekInsights = Array.from({ length: 7 }, (_, i) => {
    const date = new Date(Date.now() - i * 86400000);
    return getDailyInsight(personalYearData.personalYear, date);
  }).reverse();

  await sendEmail({
    to: user.email,
    subject: `Your Personal Year ${personalYearData.personalYear} Weekly Summary`,
    body: renderWeeklySummaryEmail(weekInsights, personalYearData),
  });
}

Production Checklist

  • Pre-calculate Personal Year for all users on January 1st
  • Cache Personal Year data until end of calendar year
  • Store calculation history for analytics
  • Implement daily notification system with time zone support
  • Create insight rotation system (avoid repetition)
  • Add streak tracking and rewards
  • Set up yearly transition celebrations
  • Implement A/B testing for notification times
  • Track engagement metrics (opens, reads, actions taken)
  • Create weekly/monthly summary emails

Next Steps

Expand your numerology features with:

  • Personal Month: Calculate monthly cycles within the year
  • Personal Day: Daily micro-cycles for detailed forecasts
  • Year-to-Year Comparisons: Show progression through cycle
  • Goal Setting: Align goals with yearly themes
  • Journaling Prompts: Reflection questions based on Personal Year

Explore complementary APIs:

Conclusion

Personal Year insights provide consistent, meaningful content that keeps users engaged year-round. Unlike daily horoscopes that require constant content creation, numerology's 9-year cycle offers a structured, predictable framework that feels both personalized and universal.

The key to success is combining accurate calculations with engaging delivery through push notifications, beautiful UI, and gamification elements like streaks and milestones.

Start with basic daily insights, measure engagement, and expand features based on user feedback. The Personal Year cycle is a powerful foundation for long-term user retention in wellness and spiritual apps.

Ready to build? Sign up for RoxyAPI and start implementing Personal Year insights today. Full API documentation at roxyapi.com/docs.