Skip to content

Resolution Modes Full Closed Beta

Every channel's egress configuration specifies a resolution mode that controls how loyalty data is structured in both webhook payloads and REST API responses. The three modes form a spectrum from simple totals to full individual records.

Closed Beta

Resolution modes are part of the Closed Beta loyalty integration. Payload schemas described here are subject to change. Contact us for access.

Overview

All three modes produce payloads that are discriminated unions on the resolution field. Your code can branch on this field to handle each mode:

typescript
function handlePayload(payload: EgressPayload) {
  switch (payload.resolution) {
    case 'aggregated':
      // payload.total_points, payload.total_occurrences
      break;
    case 'day_aggregated':
      // payload.date, payload.points, payload.occurrences
      break;
    case 'high_fidelity':
      // payload.user_action_id, payload.created_at
      break;
  }
}

Aggregated

All-time totals per user, per channel, per action. The simplest mode — each payload summarizes the running total of points and occurrences.

When to use: Point balance displays, leaderboard sync, simple dashboards. Sufficient for most integrations.

Payload Schema

typescript
interface AggregatedEgressPayload {
  resolution: 'aggregated';
  channel_id: string;
  user_id: string;
  app_action_name: string;
  total_points: number;
  total_occurrences: number;
  community_ids: string[];
  timestamp: string; // ISO 8601
}

Example

json
{
  "resolution": "aggregated",
  "channel_id": "ch_abc123",
  "user_id": "usr_xyz789",
  "app_action_name": "quiz_answer",
  "total_points": 1250,
  "total_occurrences": 47,
  "community_ids": ["com_111", "com_222"],
  "timestamp": "2025-06-15T14:32:00.000Z"
}

Field Reference

FieldTypeDescription
resolution"aggregated"Discriminator — always "aggregated" for this mode
channel_idstringThe FanFest channel ID
user_idstringThe user who performed the action
app_action_namestringThe action type (e.g., quiz_answer, poll_vote, referral)
total_pointsnumberSum of all settled points for this user + channel + action (all time)
total_occurrencesnumberTotal number of times this action was performed
community_idsstring[]Community IDs associated with the triggering action
timestampstringISO 8601 timestamp of when the payload was generated

Details

  • total_points includes only settled transactions. Transactions with on_hold status are excluded.
  • total_occurrences counts all occurrences regardless of transaction status.
  • timestamp reflects when the payload was computed, not when the action occurred.
  • In REST API responses (pull), community_ids returns an empty array for aggregated results because community data is not available from grouped database queries.

Day Aggregated

Daily bucketed totals per user, per channel, per action. Each payload summarizes the points and occurrences for a single UTC day.

When to use: Daily reconciliation, time-series reporting, syncing daily activity to external systems.

Payload Schema

typescript
interface DayAggregatedEgressPayload {
  resolution: 'day_aggregated';
  channel_id: string;
  user_id: string;
  app_action_name: string;
  date: string; // "YYYY-MM-DD" (UTC)
  points: number;
  occurrences: number;
  community_ids: string[];
}

Example

json
{
  "resolution": "day_aggregated",
  "channel_id": "ch_abc123",
  "user_id": "usr_xyz789",
  "app_action_name": "quiz_answer",
  "date": "2025-06-15",
  "points": 75,
  "occurrences": 3,
  "community_ids": ["com_111"]
}

Field Reference

FieldTypeDescription
resolution"day_aggregated"Discriminator — always "day_aggregated" for this mode
channel_idstringThe FanFest channel ID
user_idstringThe user who performed the action
app_action_namestringThe action type
datestringUTC date in YYYY-MM-DD format
pointsnumberSum of settled points for this user + channel + action on this UTC day
occurrencesnumberNumber of times this action was performed on this UTC day
community_idsstring[]Community IDs associated with the triggering action

Details

  • Day boundaries are determined by UTC, not your local timezone. An action at 2025-06-15T23:59:00Z belongs to 2025-06-15, while an action at 2025-06-16T00:01:00Z belongs to 2025-06-16.
  • points includes only settled transactions for the specified day.
  • In REST API responses (pull), community_ids returns an empty array for day-aggregated results.

High Fidelity

Individual action records with full detail. Each payload represents a single user action exactly as it was recorded.

When to use: Audit trails, real-time event streaming, detailed analytics, compliance requirements.

Payload Schema

typescript
interface HighFidelityEgressPayload {
  resolution: 'high_fidelity';
  channel_id: string;
  user_id: string;
  user_action_id: string;
  app_action_name: string;
  points: number;
  occurrences: number;
  community_ids: string[];
  created_at: string; // ISO 8601
}

Example

json
{
  "resolution": "high_fidelity",
  "channel_id": "ch_abc123",
  "user_id": "usr_xyz789",
  "user_action_id": "ua_def456",
  "app_action_name": "quiz_answer",
  "points": 25,
  "occurrences": 1,
  "community_ids": ["com_111", "com_222"],
  "created_at": "2025-06-15T14:32:00.000Z"
}

Field Reference

FieldTypeDescription
resolution"high_fidelity"Discriminator — always "high_fidelity" for this mode
channel_idstringThe FanFest channel ID
user_idstringThe user who performed the action
user_action_idstringUnique identifier for this specific action record
app_action_namestringThe action type
pointsnumberPoints awarded for this specific action (sum of settled transactions)
occurrencesnumberOccurrences recorded for this action (typically 1)
community_idsstring[]Community IDs associated with this action
created_atstringISO 8601 timestamp of when the action was originally recorded

Details

  • user_action_id uniquely identifies this action and can be used for deduplication on your side.
  • points is the sum of settled transactions associated with this specific action.
  • community_ids is fully populated in high fidelity mode (both push and pull paths).
  • This is the only mode where created_at reflects the actual action timestamp (not the payload generation time).

Comparison Table

FeatureAggregatedDay AggregatedHigh Fidelity
Data granularityAll-time totalsDaily bucketsIndividual actions
Payload sizeSmallestMediumLargest
community_ids in pull APIEmpty arrayEmpty arrayPopulated
Includes action timestampNo (generation time only)No (date only)Yes (created_at)
Includes user_action_idNoNoYes
DB queries required2 (aggregation)2 (aggregation)0 (pass-through)
Recommended forDisplay, syncReconciliation, reportingAudit, analytics

Choosing a Resolution Mode

Recommendation

Start with Aggregated if you are unsure. It is the simplest to consume, produces the smallest payloads, and is sufficient for displaying point balances and syncing leaderboard data. You can change your resolution mode later without modifying your webhook endpoint or API client — only the payload shape changes.

Upgrade to Day Aggregated if you need to reconcile daily activity between FanFest and your system, or if you produce time-series reports.

Upgrade to High Fidelity if you need a complete audit trail of every individual action, or if you are building real-time analytics on top of FanFest loyalty data.

TypeScript Types

For TypeScript projects, you can use these type definitions to handle all three resolution modes:

typescript
interface AggregatedEgressPayload {
  resolution: 'aggregated';
  channel_id: string;
  user_id: string;
  app_action_name: string;
  total_points: number;
  total_occurrences: number;
  community_ids: string[];
  timestamp: string;
}

interface DayAggregatedEgressPayload {
  resolution: 'day_aggregated';
  channel_id: string;
  user_id: string;
  app_action_name: string;
  date: string;
  points: number;
  occurrences: number;
  community_ids: string[];
}

interface HighFidelityEgressPayload {
  resolution: 'high_fidelity';
  channel_id: string;
  user_id: string;
  user_action_id: string;
  app_action_name: string;
  points: number;
  occurrences: number;
  community_ids: string[];
  created_at: string;
}

type EgressPayload =
  | AggregatedEgressPayload
  | DayAggregatedEgressPayload
  | HighFidelityEgressPayload;

The discriminated union on resolution gives you exhaustive type narrowing in switch statements and if checks.

Released under the MIT License.