Skip to main content

Prerequisites

Before submitting and verifying loyalty rule completion, you must have a user registered in Snag. Depending on the social platform, you’ll need the following user information:
  • Twitter: User’s Twitter handle and Twitter user ID
  • Telegram: User’s Telegram handle and Telegram user ID
  • Discord: User’s Discord handle and Discord user ID
For more information on user management, see:

Overview

Use the Snag API to submit and verify loyalty rule completions from your own backend. Because external verification to third-party services (e.g. Twitter, Telegram, etc.) may take up to a couple minutes, we use an asynchronous process to verify rule completion.
Note: The API-based verification process described in this recipe is only required for the following rule types:
  • Add an Item to Steam Wishlist
  • Add Text to X Bio
  • Add Text to X Username
  • Answer a Poll
  • Answer a Quiz
  • Become a GitHub Repository Collaborator
  • Check In
  • Click a Link
  • Comment on a Reddit Post
  • Comment on a YouTube Video
  • Comment on an X Post with Text
  • Complete Profile Details
  • Connect Discord
  • Connect Email
  • Connect Epic
  • Connect GitHub
  • Connect Instagram
  • Connect Phone
  • Connect Reddit
  • Connect Steam
  • Connect Telegram
  • Connect TikTok
  • Connect Wallet
  • Connect X
  • Connect Youtube
  • Enter a Code
  • External rule
  • Follow a TikTok Account
  • Follow an Instagram Account
  • Follow an X Account
  • Fork a GitHub Repository
  • Get Discord Role
  • Get X post impressions
  • Grant a Discord Role
  • Hold a Fungible Token
  • Hold an NFT (only for multiplier reward type) - this step is not required for Cadence based rules (e.g., Token Hold, Sold On … etc), as these rules run automatically on the specified start time and on set cadence (daily, weekly, monthly).
  • Join Discord Server
  • Join Telegram Group
  • Points Airdrop
  • Post on Instagram
  • Post on TikTok
  • Post on X
  • React to an X Post
  • Reward dApps by Active Users
  • Reward dApps by Gas Spent
  • Reward dApps by New Users
  • Send Discord Messages
  • Send Telegram Messages
  • Spend in Shopify Store
  • Star a GitHub Repository
  • Stratus Function
  • Submit Text Input
  • Subscribe to a YouTube Channel
  • Swap a Token
Let’s set up a rule to follow a specific Twitter account. The twitter handle or twitter user ID can be configured in the admin dashboard. We can then use the loyalty rule completion endpoint to submit a request to verify the rule completion. We can use the loyalty rule status endpoint to check the status of the rule completion request. When verification is complete, our system then creates a loyalty transaction to reward the user. The rule completion endpoint will return an error if re-called and the rule is already completed for the user. Here is an example implementation in javascript on how you can use the following three endpoints to submit and verify loyalty rule completion: Note: loyaltyRuleId accepts a single ID or an array (up to 100). For larger sets, batch requests to avoid HTTP URL length limits.
const yourServerBaseUrl = "YOUR_SERVER_BASE_URL"
const userId = "USER_ID"
const ruleId = "RULE_ID"
let isLoading = false
let success = false
// After the claim is successful, we store the transaction entries for the user in this array
let transactionEntries = []

async function postCommentOnTwitter() {
  console.log("Posted comment on Twitter!")
}

async function callCompleteEndpoint() {
  isLoading = true
  try {
    const response = await fetch(`${yourServerBaseUrl}/loyalty/complete`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      body: JSON.stringify({ userId, ruleId }),
    })
    console.log("Complete Endpoint Response:", await response.json())
    pollStatus()
  } catch (error) {
    console.error("Error completing loyalty rule:", error)
    isLoading = false
  }
}

async function pollStatus() {
  const statusUrl = `${yourServerBaseUrl}/loyalty/status?userId=${userId}`
  let attempts = 0

  async function poll() {
    try {
      const response = await fetch(statusUrl, {})
      const statuses = await response.json()
      console.log("Status Endpoint Response:", statuses)

      const ruleStatus = statuses.find((item) => item.loyaltyRuleId === ruleId)

      if (ruleStatus) {
        if (ruleStatus.status === "completed") {
          transactionEntries = await fetchTransactionEntries()
          success = true
        } else if (ruleStatus.status === "failed") {
          success = false
        } else if (attempts < 10) {
          attempts++
          setTimeout(poll, 1000)
        } else {
          success = false
        }
      } else if (attempts < 10) {
        attempts++
        setTimeout(poll, 1000)
      } else {
        success = false
      }
    } catch (error) {
      console.error("Error polling status:", error)
      success = false
    } finally {
      isLoading = false
      button.textContent = "Claim"
    }
  }

  poll()
}

async function fetchTransactionEntries() {
  try {
    const response = await fetch(
      `${yourServerBaseUrl}/loyalty/transaction-entries?userId=${userId}&userCompletedLoyaltyRuleId=${ruleId}`,
      {}
    )
    console.log("Transaction Entries Response:", await response.json())
  } catch (error) {
    console.error("Error fetching transaction entries:", error)
  }
}

async function handleClaim() {
  if (isLoading) return
  await postCommentOnTwitter()
  await callCompleteEndpoint()
}