OMX Logo
Documentation
Create Triggers

Create Geotriggers

Learn how to create advanced geotriggers with custom shapes, targeting, and complex trigger conditions.

Creating Geotriggers

Master the art of creating sophisticated geotriggers with advanced targeting, custom geometries, and complex trigger conditions for precise location-based automation.

Basic Geotrigger Creation

Start with a simple circular geotrigger:

import { GeoTrigger } from '@omx-sdk/geotrigger';

const geoTrigger = new GeoTrigger(omx);

const basicTrigger = await geoTrigger.create({
  name: "Coffee Shop Welcome",
  description: "Welcome customers entering our coffee shop",
  
  // Location definition
  geometry: {
    type: "circle",
    center: {
      latitude: 40.7128,
      longitude: -74.0060
    },
    radius: 100 // meters
  },
  
  // Trigger event
  event: "enter",
  
  // Actions to perform
  actions: [{
    type: "notification",
    config: {
      title: "Welcome!",
      body: "Get 20% off your first order!",
      data: { couponCode: "WELCOME20" }
    }
  }],
  
  // Basic settings
  enabled: true,
  priority: "normal"
});

console.log('Trigger created:', basicTrigger.id);

Advanced Geometry Types

Create triggers with complex shapes and multiple zones:

// Polygon geotrigger
const polygonTrigger = await geoTrigger.create({
  name: "Campus Zone",
  geometry: {
    type: "polygon",
    coordinates: [
      [40.7128, -74.0060], // Point 1
      [40.7150, -74.0040], // Point 2
      [40.7170, -74.0080], // Point 3
      [40.7140, -74.0100], // Point 4
      [40.7128, -74.0060]  // Close polygon
    ]
  },
  event: "enter",
  actions: [{ type: "webhook", config: { url: "/api/campus-entry" } }]
});

// Multi-zone trigger with different radii
const multiZoneTrigger = await geoTrigger.create({
  name: "Stadium Zones",
  geometry: {
    type: "multi-circle",
    zones: [
      {
        name: "entrance",
        center: { lat: 40.7128, lng: -74.0060 },
        radius: 50
      },
      {
        name: "parking",
        center: { lat: 40.7100, lng: -74.0080 },
        radius: 200
      },
      {
        name: "vip_area",
        center: { lat: 40.7140, lng: -74.0040 },
        radius: 25
      }
    ]
  },
  event: "enter",
  actions: [{
    type: "conditional",
    conditions: {
      "entrance": [{ type: "notification", config: { title: "Welcome to the stadium!" } }],
      "parking": [{ type: "notification", config: { title: "Parking zone entered" } }],
      "vip_area": [{ type: "notification", config: { title: "Welcome VIP!" } }]
    }
  }]
});

// Rectangular/bounding box trigger
const rectangularTrigger = await geoTrigger.create({
  name: "Shopping District",
  geometry: {
    type: "rectangle",
    bounds: {
      north: 40.7150,
      south: 40.7100,
      east: -74.0040,
      west: -74.0080
    }
  },
  event: "enter",
  actions: [{ type: "analytics", config: { event: "district_entry" } }]
});

Event Types and Conditions

Configure different trigger events and complex conditions:

// Multiple event types
const multiEventTrigger = await geoTrigger.create({
  name: "Store Analytics",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  
  events: [
    {
      type: "enter",
      actions: [{ type: "analytics", config: { event: "store_entry" } }]
    },
    {
      type: "exit",
      actions: [{ type: "analytics", config: { event: "store_exit" } }]
    },
    {
      type: "dwell",
      conditions: { minimumDuration: 300 }, // 5 minutes
      actions: [{ type: "notification", config: { title: "Still browsing? Here's a special offer!" } }]
    }
  ]
});

// Conditional triggers
const conditionalTrigger = await geoTrigger.create({
  name: "Time-based Coffee Shop",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  event: "enter",
  
  conditions: {
    // Time-based conditions
    timeOfDay: {
      start: "06:00",
      end: "11:00",
      timezone: "America/New_York"
    },
    
    // Day of week
    daysOfWeek: [1, 2, 3, 4, 5], // Monday to Friday
    
    // Date range
    dateRange: {
      start: "2024-01-01",
      end: "2024-12-31"
    },
    
    // Weather conditions (requires weather service integration)
    weather: {
      temperature: { min: 50, max: 80 }, // Fahrenheit
      conditions: ["clear", "partly_cloudy"]
    },
    
    // User conditions
    userAttributes: {
      isFirstVisit: true,
      customerTier: ["gold", "platinum"],
      hasAppInstalled: true
    }
  },
  
  actions: [{
    type: "notification",
    config: {
      title: "Good morning! ☀️",
      body: "Start your day with our morning special!"
    }
  }]
});

Advanced Targeting

Target specific users and segments with precision:

const targetedTrigger = await geoTrigger.create({
  name: "VIP Customer Experience",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  event: "enter",
  
  targeting: {
    // Include specific users
    includeUsers: ["user_123", "user_456"],
    
    // Exclude specific users
    excludeUsers: ["user_789"],
    
    // Target by user segments
    segments: ["vip_customers", "high_value_customers"],
    
    // Demographics targeting
    demographics: {
      age: { min: 25, max: 65 },
      gender: ["female", "male"],
      location: {
        country: ["US", "CA"],
        state: ["NY", "CA", "TX"],
        city: ["New York", "Los Angeles"]
      }
    },
    
    // Behavioral targeting
    behavior: {
      previousVisits: { min: 3 },
      averageSpend: { min: 50 },
      lastVisit: { within: "30 days" },
      preferences: ["coffee", "pastries"],
      loyaltyStatus: ["gold", "platinum"]
    },
    
    // Device targeting
    device: {
      platform: ["ios", "android"],
      osVersion: { min: "13.0" },
      appVersion: { min: "2.0.0" },
      hasLocationPermission: true,
      hasNotificationPermission: true
    },
    
    // Geofencing targeting
    location: {
      // Only trigger for users who live/work nearby
      homeLocation: {
        within: 10000, // 10km radius
        center: { lat: 40.7128, lng: -74.0060 }
      },
      workLocation: {
        within: 5000 // 5km radius
      }
    }
  },
  
  actions: [{
    type: "personalized_notification",
    config: {
      template: "vip_welcome",
      data: {
        offerType: "exclusive",
        discountPercent: 30
      }
    }
  }]
});

Action Types and Configurations

Configure various actions to execute when triggers fire:

const actionRichTrigger = await geoTrigger.create({
  name: "Multi-Action Store Experience",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  event: "enter",
  
  actions: [
    // Send push notification
    {
      type: "notification",
      config: {
        title: "Welcome to our store!",
        body: "Check out today's special offers",
        icon: "store_icon",
        badge: 1,
        sound: "notification.wav",
        clickAction: "open_app",
        data: {
          screen: "offers",
          storeId: "store_123"
        }
      }
    },
    
    // Send email
    {
      type: "email",
      config: {
        template: "store_visit_welcome",
        data: {
          storeName: "Downtown Coffee",
          visitTime: "{{now}}",
          offers: ["20% off coffee", "Free pastry with purchase"]
        }
      }
    },
    
    // Send SMS
    {
      type: "sms",
      config: {
        message: "Welcome! Show this text for 10% off your purchase.",
        shortCode: "WELCOME10"
      }
    },
    
    // Call webhook
    {
      type: "webhook",
      config: {
        url: "https://api.yourstore.com/customer-entered",
        method: "POST",
        headers: {
          "Authorization": "Bearer {{api_token}}",
          "Content-Type": "application/json"
        },
        body: {
          userId: "{{user.id}}",
          storeId: "store_123",
          timestamp: "{{now}}",
          location: "{{trigger.location}}"
        },
        timeout: 5000,
        retries: 3
      }
    },
    
    // Update user profile
    {
      type: "update_profile",
      config: {
        attributes: {
          lastStoreVisit: "{{now}}",
          visitCount: "{{increment}}",
          preferredStore: "store_123"
        }
      }
    },
    
    // Track analytics event
    {
      type: "analytics",
      config: {
        event: "store_entry",
        properties: {
          storeId: "store_123",
          entryTime: "{{now}}",
          userTier: "{{user.tier}}",
          weather: "{{weather.condition}}"
        }
      }
    },
    
    // Add to audience/segment
    {
      type: "add_to_segment",
      config: {
        segment: "store_visitors_2024"
      }
    }
  ]
});

Trigger Settings and Optimization

Fine-tune trigger behavior and performance:

const optimizedTrigger = await geoTrigger.create({
  name: "Optimized Restaurant Trigger",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  event: "enter",
  
  settings: {
    // Trigger frequency limits
    maxTriggersPerUser: 3,        // Max triggers per user per day
    maxTriggersPerHour: 1,        // Max triggers per user per hour
    cooldownPeriod: 3600,         // 1 hour cooldown between triggers
    
    // Performance settings
    priority: "high",             // "low", "normal", "high", "critical"
    batchProcessing: false,       // Process immediately vs batch
    
    // Accuracy settings
    minAccuracy: 50,              // Minimum GPS accuracy in meters
    dwellTime: 30,                // Minimum time in zone before triggering (seconds)
    exitBuffer: 20,               // Buffer zone to prevent rapid enter/exit
    
    // Retry and error handling
    retryFailedActions: true,
    maxActionRetries: 3,
    failureNotification: {
      webhook: "https://api.yourapp.com/trigger-failures"
    },
    
    // Expiration
    expiresAt: "2024-12-31T23:59:59Z",
    autoDisableAfter: 1000,       // Disable after 1000 triggers
    
    // Debugging
    debug: false,
    logLevel: "info",
    trackingEnabled: true
  },
  
  actions: [{
    type: "notification",
    config: {
      title: "Welcome to Tony's Restaurant!",
      body: "Try our chef's special today!"
    }
  }]
});

Bulk Creation and Templates

Create multiple triggers efficiently using templates:

// Create multiple triggers from template
const storeLocations = [
  { id: "store_1", name: "Downtown", lat: 40.7128, lng: -74.0060 },
  { id: "store_2", name: "Midtown", lat: 40.7589, lng: -73.9851 },
  { id: "store_3", name: "Brooklyn", lat: 40.6782, lng: -73.9442 }
];

const triggerTemplate = {
  geometry: { type: "circle", radius: 100 },
  event: "enter",
  actions: [{
    type: "notification",
    config: {
      title: "Welcome to {{storeName}}!",
      body: "Check out our daily specials"
    }
  }],
  settings: {
    maxTriggersPerUser: 2,
    cooldownPeriod: 7200 // 2 hours
  }
};

// Bulk create triggers
const triggers = await geoTrigger.createBatch(
  storeLocations.map(store => ({
    ...triggerTemplate,
    name: `${store.name} Store Welcome`,
    geometry: {
      ...triggerTemplate.geometry,
      center: { lat: store.lat, lng: store.lng }
    },
    actions: [{
      ...triggerTemplate.actions[0],
      config: {
        ...triggerTemplate.actions[0].config,
        title: triggerTemplate.actions[0].config.title.replace('{{storeName}}', store.name)
      }
    }],
    metadata: {
      storeId: store.id,
      storeType: "retail"
    }
  }))
);

console.log(`Created ${triggers.length} triggers`);

Validation and Testing

Validate triggers before deployment:

// Validate trigger configuration
const validation = await geoTrigger.validate({
  name: "Test Trigger",
  geometry: { type: "circle", center: { lat: 40.7128, lng: -74.0060 }, radius: 100 },
  event: "enter",
  actions: [{ type: "notification", config: { title: "Test" } }]
});

if (!validation.valid) {
  console.error('Validation errors:', validation.errors);
  return;
}

// Test trigger with mock data
const testResult = await geoTrigger.test(triggerId, {
  userId: "test_user",
  location: { lat: 40.7130, lng: -74.0062 },
  timestamp: Date.now(),
  mockWeather: { condition: "clear", temperature: 72 }
});

console.log('Test result:', testResult);

// Dry run (validate without creating)
const dryRun = await geoTrigger.dryRun({
  // ... trigger configuration
}, {
  checkGeometry: true,
  validateActions: true,
  estimateCosts: true
});

console.log('Dry run results:', dryRun);

Best Practices

  • Start Simple: Begin with basic circular triggers before moving to complex geometries
  • Test Thoroughly: Always test triggers in staging before production deployment
  • Optimize Radius: Use 50-200 meter radius for most use cases
  • Set Cooldowns: Prevent trigger spam with appropriate cooldown periods
  • Monitor Performance: Track trigger performance and adjust settings as needed
  • User Privacy: Always obtain proper consent for location tracking
  • Battery Optimization: Limit the number of active triggers per user

Next Steps