Overview

When implementing loyalty rules in your application, you often need to know whether a user has already completed a rule and received rewards. This information is useful for:

  • Displaying completed rules differently in your UI
  • Preventing users from attempting to complete already-completed rules
  • Building custom reward history displays

Snag provides a straightforward way to check rule completion and reward status using the transaction entries endpoint.

Using Transaction Entries to Verify Rule Completion

The transaction entries API endpoint allows you to check whether a user has completed specific rules by looking for reward transactions associated with those rules.

API Endpoint

GET /api/loyalty/transaction_entries

Required Parameters

ParameterTypeDescription
userCompletedLoyaltyRuleIdstring[]Array of loyalty rule IDs to check for completion
userIdstringThe ID of the user to check

Key Behavior

Important: When using the userCompletedLoyaltyRuleId parameter, the endpoint will return only the latest transaction entry for each specified rule ID. This optimized behavior makes it ideal for quickly checking completion status without filtering through multiple entries per rule.

Example Request

const fetchRuleCompletionStatus = async (userId, ruleIds) => {
  const queryParams = new URLSearchParams({
    userId: userId,
  });
  
  // Add each rule ID to the query parameters
  ruleIds.forEach(ruleId => {
    queryParams.append('userCompletedLoyaltyRuleId', ruleId);
  });
  
  const response = await fetch(
    `${API_BASE_URL}/api/loyalty/transaction_entries?${queryParams.toString()}`,
    {
      method: 'GET',
      headers: {
        'Authorization': `Bearer ${API_KEY}`,
        'Content-Type': 'application/json'
      }
    }
  );
  
  return await response.json();
};

Interpreting Results

The response contains an array of transaction entries, each representing the most recent reward transaction for a completed rule:

  1. Rule is completed: If a transaction entry exists for a specific rule ID, the user has completed that rule and received rewards.

  2. Rule is not completed: If no transaction entry exists for a rule ID, the user has not completed that rule or has not received rewards for it.

  3. Latest entries only: Since the userCompletedLoyaltyRuleId parameter is used, the endpoint returns only the latest entry per rule, you’ll always see the most recent completion status, which is particularly useful for rules that can be completed multiple times (like daily check-ins).

Implementation Example

Here’s a complete example showing how to check if a user has completed specific rules and display the appropriate UI:

// Check if user has completed specific rules
async function checkRuleCompletionStatus(userId, ruleIds) {
  try {
    const queryParams = new URLSearchParams({ userId });
    ruleIds.forEach(ruleId => {
      queryParams.append('userCompletedLoyaltyRuleId', ruleId);
    });
    
    const response = await fetch(
      `${API_BASE_URL}/api/loyalty/transaction_entries?${queryParams.toString()}`,
      {
        headers: {
          'Authorization': `Bearer ${API_KEY}`,
          'Content-Type': 'application/json'
        }
      }
    );
    
    const { data } = await response.json();
    
    // Create a map of rule IDs to completion status
    const completionStatus = {};
    ruleIds.forEach(ruleId => {
      completionStatus[ruleId] = false;
    });
    
    // Update completion status based on transaction entries
    // Each entry is guaranteed to be the latest for that rule
    data.forEach(entry => {
      if (entry.userCompletedLoyaltyRuleId && 
          ruleIds.includes(entry.userCompletedLoyaltyRuleId)) {
        completionStatus[entry.userCompletedLoyaltyRuleId] = true;
      }
    });
    
    return completionStatus;
  } catch (error) {
    console.error('Error checking rule completion status:', error);
    throw error;
  }
}

// Example usage in a component
function QuestsComponent({ userId, quests }) {
  const [completionStatus, setCompletionStatus] = useState({});
  const [isLoading, setIsLoading] = useState(true);
  
  useEffect(() => {
    const ruleIds = quests.map(quest => quest.ruleId);
    
    checkRuleCompletionStatus(userId, ruleIds)
      .then(status => {
        setCompletionStatus(status);
        setIsLoading(false);
      })
      .catch(() => {
        setIsLoading(false);
      });
  }, [userId, quests]);
  
  if (isLoading) {
    return <div>Loading quests...</div>;
  }
  
  return (
    <div className="quests-container">
      {quests.map(quest => (
        <QuestItem 
          key={quest.ruleId}
          quest={quest}
          isCompleted={completionStatus[quest.ruleId]}
        />
      ))}
    </div>
  );
}

For more information on handling rule completion, refer to:

Limitations and Considerations

  • For some rule types (like cadence-based rules), completion status may update on a schedule rather than immediately
  • Using the userCompletedLoyaltyRuleId parameter, the endpoint returns only the latest entry per rule, making it efficient for checking current completion status
  • Cache results where appropriate to reduce API calls in your application