Public Feeds

Public Campaign Feeds provide JSON feeds of available campaigns for your organization. These feeds are designed for mass user access via CloudFront CDN and can be embedded directly in your web or mobile applications.

Overview

  • CloudFront Distribution: Fast global access with edge caching

  • JSON Format: Easy to parse and integrate

  • Time-based Filtering: View past, current, or future campaigns

  • Tag Filtering: Filter campaigns by tags

  • Public Access: No authentication required

  • Auto-refresh: Feeds update automatically when campaigns change

Feed Types

Current Campaigns

Get all currently active campaigns.

URL: https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/current.json

Returns campaigns where startDate <= now <= endDate.

Upcoming Campaigns

Get future campaigns that haven't started yet.

URL: https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/upcoming.json

Returns campaigns where startDate > now.

Past Campaigns

Get campaigns that have ended.

URL: https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/past.json

Returns campaigns where endDate < now.

All Campaigns

Get all published campaigns (past, current, and future).

URL: https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/all.json

Returns all campaigns with status: 'published'.

Query Parameters

All feeds support the following optional parameters:

Parameter
Type
Description
Example

tags

string

Comma-separated list of tags to filter by

?tags=featured,seasonal

includeUnpublished

boolean

Include draft/unpublished campaigns (requires config)

?includeUnpublished=true

limit

number

Max campaigns to return (default: 100)

?limit=50

Tag Filtering

Tag filtering uses OR logic - campaigns matching ANY of the specified tags will be included.

Examples:

# Featured campaigns only
/campaigns/current.json?tags=featured

# Featured OR seasonal campaigns
/campaigns/current.json?tags=featured,seasonal

# VIP campaigns in the past
/campaigns/past.json?tags=vip

Response Format

{
  "orgId": "550e8400-e29b-41d4-a716-446655440000",
  "feedType": "current",
  "generatedAt": "2025-10-06T15:00:00Z",
  "cacheExpiresAt": "2025-10-06T15:15:00Z",
  "filters": {
    "tags": ["featured"],
    "includeUnpublished": false
  },
  "campaigns": [
    {
      "campaignId": "camp_abc123",
      "name": "Weekend Bonus Points",
      "description": "Earn double points on all activities this weekend!",
      "type": "points_multiplier",
      "status": "active",
      "startDate": "2025-10-05T00:00:00Z",
      "endDate": "2025-10-07T23:59:59Z",
      "tags": ["featured", "weekend"],
      "triggers": [
        {
          "eventType": "user.activity",
          "conditions": []
        }
      ],
      "effects": [
        {
          "type": "add_points",
          "config": {
            "amount": 100,
            "multiplier": 2,
            "reason": "Weekend Bonus"
          }
        }
      ],
      "metadata": {
        "imageUrl": "https://cdn.monterosa.cloud/campaigns/weekend-bonus.jpg",
        "bannerColor": "#FF6B35",
        "priority": 1
      },
      "segmentIds": [],
      "createdAt": "2025-10-01T10:00:00Z",
      "updatedAt": "2025-10-05T09:00:00Z"
    }
  ],
  "count": 1,
  "hasMore": false
}

Field Descriptions

Campaign Fields

Field
Type
Description

campaignId

string

Unique campaign identifier

name

string

Campaign display name

description

string

Campaign description

type

string

Campaign type (e.g., points_multiplier, streak_bonus)

status

string

Campaign status (active, scheduled, completed)

startDate

string

When campaign starts (ISO 8601)

endDate

string

When campaign ends (ISO 8601)

tags

array

Tags for filtering and categorization

triggers

array

Event triggers that activate the campaign

effects

array

Rewards and actions when triggered

metadata

object

Custom display properties (images, colors, etc.)

segmentIds

array

User segments this campaign applies to

Integration Examples

JavaScript / React

async function fetchCurrentCampaigns(orgId, tags = []) {
  const url = new URL(`https://cdn.monterosa.cloud/loyalty/${orgId}/campaigns/current.json`);

  if (tags.length > 0) {
    url.searchParams.set('tags', tags.join(','));
  }

  const response = await fetch(url);
  const data = await response.json();

  return data.campaigns;
}

// Usage
const campaigns = await fetchCurrentCampaigns('your-org-id', ['featured']);

// Display in React
function CampaignList() {
  const [campaigns, setCampaigns] = useState([]);

  useEffect(() => {
    fetchCurrentCampaigns('your-org-id')
      .then(setCampaigns);
  }, []);

  return (
    <div>
      {campaigns.map(campaign => (
        <div key={campaign.campaignId}>
          <h3>{campaign.name}</h3>
          <p>{campaign.description}</p>
          <span>Ends: {new Date(campaign.endDate).toLocaleDateString()}</span>
        </div>
      ))}
    </div>
  );
}

Swift / iOS

struct Campaign: Codable {
    let campaignId: String
    let name: String
    let description: String
    let startDate: String
    let endDate: String
    let tags: [String]
}

struct CampaignFeed: Codable {
    let orgId: String
    let campaigns: [Campaign]
    let count: Int
}

func fetchCampaigns(orgId: String, tags: [String] = []) async throws -> [Campaign] {
    var components = URLComponents(string: "https://cdn.monterosa.cloud/loyalty/\(orgId)/campaigns/current.json")!

    if !tags.isEmpty {
        components.queryItems = [
            URLQueryItem(name: "tags", value: tags.joined(separator: ","))
        ]
    }

    let (data, _) = try await URLSession.shared.data(from: components.url!)
    let feed = try JSONDecoder().decode(CampaignFeed.self, from: data)

    return feed.campaigns
}

Kotlin / Android

data class Campaign(
    val campaignId: String,
    val name: String,
    val description: String,
    val startDate: String,
    val endDate: String,
    val tags: List<String>
)

data class CampaignFeed(
    val orgId: String,
    val campaigns: List<Campaign>,
    val count: Int
)

suspend fun fetchCampaigns(orgId: String, tags: List<String> = emptyList()): List<Campaign> {
    val url = buildString {
        append("https://cdn.monterosa.cloud/loyalty/$orgId/campaigns/current.json")
        if (tags.isNotEmpty()) {
            append("?tags=")
            append(tags.joinToString(","))
        }
    }

    val response = httpClient.get(url)
    val feed = response.body<CampaignFeed>()

    return feed.campaigns
}

Caching

CloudFront Cache Behavior

  • Cache Duration: 15 minutes (configurable)

  • Cache Key: URL + query parameters

  • Invalidation: Automatic on campaign publish/update

Client-Side Caching

The cacheExpiresAt field indicates when the client should refresh:

const CACHE_KEY = 'loyalty_campaigns';

async function getCachedCampaigns(orgId) {
  const cached = localStorage.getItem(CACHE_KEY);

  if (cached) {
    const data = JSON.parse(cached);

    // Check if cache is still valid
    if (new Date(data.cacheExpiresAt) > new Date()) {
      return data.campaigns;
    }
  }

  // Fetch fresh data
  const response = await fetch(`https://cdn.monterosa.cloud/loyalty/${orgId}/campaigns/current.json`);
  const data = await response.json();

  // Store in cache
  localStorage.setItem(CACHE_KEY, JSON.stringify(data));

  return data.campaigns;
}

Unpublished Campaigns

By default, only campaigns with status: 'published' are included in feeds. To include draft/unpublished campaigns, set includeUnpublished=true.

⚠️ Important: This feature must be enabled in your organization settings. Contact support to enable.

/campaigns/all.json?includeUnpublished=true

When enabled, campaigns with any status will be included, allowing you to preview upcoming campaigns before publishing.

CORS Configuration

All feeds include CORS headers for cross-origin access:

Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, HEAD
Access-Control-Max-Age: 3600

This allows feeds to be loaded from any domain.

Best Practices

Performance

  • Cache aggressively: Use the provided cacheExpiresAt timestamp

  • Implement ETags: Check ETag header to avoid unnecessary transfers

  • Use CDN: Always use the CloudFront URL, not the origin API

Display

  • Show end dates: Display campaign end dates prominently

  • Filter by relevance: Use tags to show relevant campaigns to users

  • Sort by priority: Use metadata.priority for display order

  • Handle images: Load campaign images from metadata.imageUrl

Error Handling

async function fetchCampaignsWithFallback(orgId) {
  try {
    const response = await fetch(`https://cdn.monterosa.cloud/loyalty/${orgId}/campaigns/current.json`);

    if (!response.ok) {
      throw new Error(`HTTP ${response.status}`);
    }

    return await response.json();
  } catch (error) {
    console.error('Failed to fetch campaigns:', error);

    // Return cached data as fallback
    const cached = localStorage.getItem('loyalty_campaigns_fallback');
    return cached ? JSON.parse(cached) : { campaigns: [] };
  }
}

Feed URLs Reference

Replace {orgId} with your organization ID:

# Current campaigns
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/current.json

# Upcoming campaigns
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/upcoming.json

# Past campaigns
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/past.json

# All campaigns
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/all.json

# With tag filtering
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/current.json?tags=featured,vip

# With unpublished campaigns (if enabled)
https://cdn.monterosa.cloud/loyalty/{orgId}/campaigns/all.json?includeUnpublished=true

Monitoring

Track feed usage in your analytics:

// Google Analytics example
async function fetchAndTrackCampaigns(orgId) {
  const start = Date.now();

  try {
    const data = await fetch(`https://cdn.monterosa.cloud/loyalty/${orgId}/campaigns/current.json`).then(r => r.json());

    // Track successful load
    gtag('event', 'campaign_feed_loaded', {
      feed_type: 'current',
      campaign_count: data.count,
      load_time: Date.now() - start
    });

    return data.campaigns;
  } catch (error) {
    // Track errors
    gtag('event', 'campaign_feed_error', {
      error_type: error.message
    });

    throw error;
  }
}

Troubleshooting

Feed not updating

  • Wait up to 15 minutes for CDN cache to expire

  • Check if campaign was actually published

  • Verify campaign dates are correct

CORS errors

  • Ensure you're using the CDN URL, not the API origin

  • Check browser console for specific CORS error

  • Verify your domain is allowlisted (if using restricted CORS)

Missing campaigns

  • Check campaign status is 'published'

  • Verify campaign dates match the feed type (current/upcoming/past)

  • Check if tags filter is too restrictive

Slow loads

  • Implement client-side caching

  • Use CDN edge locations closest to users

  • Consider using a service worker for offline support

Last updated