Snag Docs logo

Create loyalty rules using API

Learn how to create different types of loyalty rules through the API with the correct parameters and metadata

Using an AI coding assistant? Connect Cursor, Claude, or Copilot to Snag Docs for context-aware help. Learn how.

Overview

Loyalty rules define how users earn points, multipliers, or badges in your loyalty program. Each rule type has specific requirements for parameters, metadata, and configuration. This guide covers all available rule types and their required fields.

Common Parameters

All loyalty rules share these common parameters:

organizationIdstringrequired

The organization ID that owns this rule.

websiteIdstringrequired

The website ID where this rule will be applied.

namestringrequired

The name of the loyalty rule (max 255 characters).

descriptionstring

Optional description of the rule.

typeLoyaltyRuleTyperequired

The type of loyalty rule. See available types below.

frequencyLoyaltyRuleFrequencyrequired

How often eligibility is checked. Options: once, daily, weekly, monthly, immediately.

intervalLoyaltyRuleInterval

How often users can earn rewards. Options: once, daily, weekly, monthly, unlimited, custom.

startTimeISO8601 stringrequired

When the rule becomes active (ISO 8601 format).

endTimeISO8601 string

Optional end time for the rule (ISO 8601 format).

rewardTypeLoyaltyRuleRewardTyperequired

Type of reward: points, multiplier, or badge.

loyaltyCurrencyIdstringrequired

The ID of the loyalty currency (points) to reward.

networkNetworkTyperequired

The blockchain network. Options: mainnet, goerli, polygon, arbitrum, etc.

Reward Configuration

Reward Types

Reward users with points for completing actions.

amountnumberrequired

The number of points to reward.

loyaltyCurrencyIdstringrequired

ID of the currency to reward with.

Rule Types

Rule types are grouped below to match the categories shown in the loyalty dashboard. Each type only reads the metadata (and top-level) fields listed for it — omit the rest.

User Onboarding

Complete Profile Details (profile_completed)

Reward users for completing their profile.

metadata.completeProfileConditionsRecord<string, boolean>

Which profile fields must be filled. Keys: profile_picture, name, location, nft_portfolio, about, email, twitter, discord, telegram, evm, solana, sui, ton, imx. If omitted, a default set is checked (bio, avatar, X, Discord, email).

Refer a Friend (referred_user)

Reward users for referring others.

amountnumberrequired

Points for the referred user.

metadata.referrerRewardnumberrequired

Points for the referrer.

metadata.referrerRewardLoyaltyCurrencyIdstring

Currency for referrer reward.

metadata.referralRequirements.achievePointsboolean

Require referred users to achieve points.

metadata.referralRequirements.points.amountnumber

Required points when achievePoints is true.

metadata.referralRequirements.completeProfileboolean

Require complete profile.

metadata.referralRequirements.connectTwitterboolean

Require Twitter connection.

metadata.referralRequirements.connectDiscordboolean

Require Discord connection.

metadata.referralRequirements.connectEmailboolean

Require email connection.

Check In (check_in)

Reward users for checking in.

amountnumberrequired

Points for each check-in.

metadata.enableStreaksboolean

Enable streak tracking for consecutive check-ins.

metadata.streakArrayArray<Streak>

Optional array of streak milestones and rewards.

streakMilestonenumberrequired

Number of consecutive days for this streak.

streakAmountnumberrequired

Points awarded for reaching this milestone.

Connect Accounts

Reward users for connecting an account. Most connection rules need no metadata — pass an empty object "metadata": {}.

Rule typeMetadata
connected_emailNone — pass {}
connected_phoneNone — pass {}
connected_telegramNone — pass {}
connected_tiktokNone — pass {}
connected_githubNone — pass {}
connected_youtubeNone — pass {}
connected_redditNone — pass {}
connected_instagramNone — pass {}
connected_steamNone — pass {}
connected_epicNone — pass {}

The remaining connection rules take metadata:

Connect X (connected_twitter)

metadata.enableVerifiedMultiplierboolean

Apply a higher reward to verified (blue-check) accounts.

metadata.verifiedMultipliernumber

Multiplier applied when enableVerifiedMultiplier is true (e.g. 1.5).

Connect Discord (connected_discord)

metadata.enableJoinDiscordServersboolean

Require users to also join specific Discord servers.

metadata.discordServersToJoinArray<DiscordServer>

Servers (and optionally channels/roles) users must join.

idstringrequired

Discord server ID.

channelsArray<Channel>

Channels to verify activity in.

rolesArray<Role>

Optional roles to verify.

Connect Wallet (connect_wallet)

metadata.walletTypeWalletTyperequired

Which wallet type the user must connect (e.g. evm, solana).

Social Quests

X (Twitter) engagement rules generally require oauthCredentialsId (top-level) so the platform can verify activity, and many accept an optional verified-account multiplier (metadata.enableVerifiedMultiplier, metadata.verifiedMultiplier).

React to an X Post (drip_x_tweet)

metadata.twitterPostUrlstringrequired

URL of the X post to engage with.

metadata.twitterUserIdstringrequired

Twitter user ID of the post author.

metadata.checkLikeboolean

Require liking the post.

metadata.checkRepostboolean

Require reposting.

metadata.checkCommentboolean

Require commenting.

oauthCredentialsIdstring

Twitter OAuth credentials (top-level field).

Post on X (drip_x_new_tweet)

metadata.checkTextstring | Array<string>

Text that must be included in the tweet.

metadata.requirePostLinkboolean

Require users to submit post link (default: true).

metadata.requirePostMediaLinkboolean

Require post to include media.

Follow an X Account (drip_x_follow, twitter_follow)

metadata.twitterUsernamestringrequired

X account to follow.

metadata.twitterUserIdstringrequired

Twitter user ID of the account.

oauthCredentialsIdstringrequired

OAuth credentials for Twitter (top-level field).

drip_x_follow accepts the account as metadata.twitterAccountUrl (full profile URL) instead of username/ID.

Add Text to X Bio / Username / Comment (drip_x_text_in_bio, drip_x_text_in_name, drip_x_text_in_comment)

metadata.checkTextstring | Array<string>required

Text that must appear in the user's bio, display name, or comment.

metadata.rewardQualityPostsboolean

(drip_x_text_in_comment) Only reward posts that pass quality/sentiment checks.

metadata.minimumFollowerCountnumber

(drip_x_text_in_comment) Minimum follower count to qualify.

Reach X Followers (twitter_followers)

metadata.rangeArray<RewardAmountRange>required

Tiered rewards by the user's follower count.

Get X Post Impressions (post_impressions)

metadata.rangeArray<RewardAmountRange>required

Tiered rewards by post view/impression count.

metadata.rewardQualityPostsboolean

Only reward posts that pass quality/sentiment checks.

metadata.minimumFollowerCountnumber

Minimum follower count to qualify.

trackProgressboolean

Optional top-level flag to track progress. mediaUrl (top-level) can supply a media reference.

Repost an X Post (Retweet)

metadata.twitterUserIdstringrequired

Twitter user ID of the account whose posts are monitored for reposts.

Like an X Post (twitter_like)

metadata.twitterUsernamestringrequired

Account that posted the tweet.

metadata.twitterUserIdstringrequired

Twitter user ID.

oauthCredentialsIdstringrequired

OAuth credentials (top-level field).

Comment on an X Post (twitter_comment)

metadata.twitterUserIdstringrequired

Twitter user ID of the account whose posts are monitored for comments.

Post on X with Hashtag (twitter_post_hashtag)

metadata.twitterHashtagstringrequired

Hashtag to search for in recent posts.

Get an X Post Liked by Project (tweet_liked_by_project)

metadata.twitterUserIdstringrequired

Twitter user ID of the project account whose like qualifies the user.

oauthCredentialsIdstringrequired

OAuth credentials for Twitter (top-level field).

metadata.rewardPerImpressionboolean

Multiply the reward by the tweet's impression count.

Get Discord Role (discord_member)

metadata.discordServersToJoinArray<DiscordServer>required

Servers and roles to verify. Each item uses id and roles.

Join Discord Server (discord_join)

metadata.discordServersToJoinArray<DiscordServer>required

Servers the user must join. Only the server id is required for this type.

Send Discord Messages (DiscordMessages)

metadata.discordServersToJoinArray<DiscordServer>required

Server id and the channels whose messages count. Each channel can specify text to match and emojis reactions.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards based on message count.

Grant a Discord Role (discord_role_grant)

metadata.discordServersToJoinArray<DiscordServer>required

Server id plus the roles to grant. roles is required for this type.

idstringrequired

Discord server (guild) ID.

channelsArray<Channel>

Channels to check. Each: { id: string, text?: string, emojis?: Array<{ id: string }> }. Used by DiscordMessages.

rolesArray<{ id: string }>

Roles to verify or grant. Required for discord_role_grant.

Join Telegram Group (telegram_join)

metadata.telegramChannelIdstringrequired

ID of the Telegram channel the user must join.

Send Telegram Messages (telegram_messages)

metadata.telegramChannelIdstringrequired

ID of the Telegram channel whose messages count.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards based on message count.

Follow a TikTok Account (tiktok_follow)

metadata.twitterAccountUrlstringrequired

TikTok profile URL to follow (e.g. https://www.tiktok.com/@username). Despite the field name, this holds the TikTok URL.

Post on TikTok (tiktok_post)

metadata.checkTextstring | Array<string>

Text to check in post.

metadata.requirePostLinkboolean

Require post link submission.

metadata.rewardCriteriaLoyaltyPostRewardCriteria

IMPRESSIONS_COUNT or ELIGIBLE_POST.

Post on Instagram (instagram_post)

metadata.checkTextstring | Array<string>required

Text or hashtag the post must include.

metadata.rewardQualityPostsboolean

Only reward posts that pass quality checks.

metadata.minimumFollowerCountnumber

Minimum follower count to qualify.

Follow an Instagram Account (instagram_follow)

metadata.twitterAccountUrlstringrequired

Instagram profile URL to follow (e.g. https://www.instagram.com/username). Despite the field name, this holds the Instagram URL.

Subscribe to a YouTube Channel (youtube_subscribers)

metadata.youtubeChannelIdstringrequired

YouTube channel ID the user must subscribe to.

externalIntegrationIdstring

Optional Google API integration ID (top-level field) used for verification.

Comment on a YouTube Video (youtube_comment)

metadata.youtubeVideoIdstringrequired

YouTube video ID the user must comment on.

externalIntegrationIdstring

Optional Google API integration ID (top-level field) used for verification.

Comment on a Reddit Post (reddit_comment)

metadata.redditPostIdstringrequired

ID of the Reddit post users must comment on.

metadata.cta.hrefstringrequired

Full URL of the Reddit post.

Add an Item to Steam Wishlist (steam_wishlist)

metadata.steamAppIdstringrequired

Steam application ID the user must wishlist.

Spend in Shopify Store (shopify_spend)

shopifyStoreUrlstringrequired

Shopify store domain (top-level field).

shopifyStoreAccessTokenstringrequired

Shopify admin API access token (top-level field).

metadata.rangeArray<RewardAmountRange>

Range-based rewards by purchase amount.

Token Activity

Mint an NFT (MintOn)

Reward users for minting NFTs.

collectionsArray<Collection>required

Collections to track (top-level field).

metadata.hasNeverSoldboolean

Only reward users who have never sold the token.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards.

Purchase an NFT (BoughtOn)

Reward users for purchasing NFTs.

collectionsArray<Collection>required

Collections to track (top-level field).

metadata.onlyRewardExistingUserboolean

Only reward existing users.

metadata.hasNeverSoldboolean

Only reward users who never sold the token.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards.

{
  "name": "NFT Purchase Rewards",
  "type": "BoughtOn",
  "network": "mainnet",
  "collections": [{ "address": "0x...", "network": "mainnet" }],
  "frequency": "daily",
  "interval": "once",
  "startTime": "2024-01-01T00:00:00Z",
  "rewardType": "points",
  "loyaltyCurrencyId": "currency_123",
  "amount": 1000,
  "metadata": {
    "onlyRewardExistingUser": false,
    "hasNeverSold": false
  }
}

Hold an NFT (TokenHold)

Reward users for holding specific NFTs.

collectionsArray<Collection>required

Collections to track (top-level field).

metadata.onlyRewardExistingUserboolean

Only reward existing users.

metadata.onlyRewardSingleTokenOwnershipboolean

Reward only one token ownership per contract.

metadata.rangeArray<RewardAmountRange>

Tiered rewards by number of tokens held.

{
  "name": "Hold Blue Chip NFT",
  "type": "TokenHold",
  "network": "mainnet",
  "collections": [
    {
      "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
      "network": "mainnet"
    }
  ],
  "frequency": "daily",
  "interval": "daily",
  "startTime": "2024-01-01T00:00:00Z",
  "rewardType": "points",
  "loyaltyCurrencyId": "currency_123",
  "amount": 100,
  "metadata": {
    "onlyRewardExistingUser": false,
    "onlyRewardSingleTokenOwnership": false
  }
}

Sell an NFT (SoldOn)

Reward users for selling NFTs.

metadata.hasSaleCurrencyboolean

Filter by specific sale currency.

metadata.saleCurrencystring

Currency address when hasSaleCurrency is true.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards.

Hold a Fungible Token (token_hold_erc20)

Reward users for holding ERC20 tokens.

metadata.rewardPerValueboolean

Reward based on USD value held rather than token count.

metadata.isTokenHoldMultiplierboolean

Apply a multiplier per holding tier instead of fixed points.

metadata.rangeArray<RewardAmountRange>

Tiered rewards by amount held.

Swap a Token (swap)

Reward users for token swaps.

metadata.swap.providerstring

any, relay, or lifi.

metadata.swap.relayReferrerIdstring

Relay referrer ID when provider is relay.

metadata.swap.fromobject

Source-token filter: { mode: "any" | "specific", chain, tokens, trackAmount }.

metadata.swap.toobject

Destination-token filter: { mode: "any" | "specific", chain, tokens, trackAmount }.

metadata.swap.requireCrossChainSwapboolean

Require cross-chain swaps.

Hold tokens in Robinhood (robinhood_token_hold)

metadata.robinhoodSymbolsArray<string>required

Token symbols to track (e.g. ["TRUMP", "SPY"]).

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards by holding amount. Falls back to top-level amount if omitted.

Complex Onchain Logic

Smart Contract Event (smart_contract_event)

Reward based on smart contract events.

collectionAddressstringrequired

Contract address to monitor (top-level field).

metadata.smartContract.eventstring

Event signature to track.

metadata.smartContract.criteriaSmartContractCriteria

everyEvent or specify conditions.

metadata.smartContract.paramsArray<Param>

Event parameter filters.

metadata.smartContract.addressMappingstring

Address parameter name in event.

Stratus Function (stratus_function)

Custom on-chain logic executed by a Stratus function; the function controls reward output.

functionIdstringrequired

ID of the Stratus function to run (top-level field).

subscriptionIdstring

Optional Stratus subscription ID (top-level field).

isDappRuleboolean

Optionally route rewards to the dApp owner. When true, rewardType must be points and network is required.

Provide Liquidity on Uniswap V2 / V3 (liquidity_uniswap_v2, liquidity_uniswap_v3)

metadata.liquidity.networkNetworkTyperequired

Chain for liquidity pools.

metadata.liquidity.protocolstringrequired

Protocol identifier (e.g., "uniswap-v2/eth-usdc").

metadata.liquidity.poolsArray<Pool>

Array of liquidity pool addresses (max 20, same network/protocol).

metadata.liquidity.calculationTypeLiquidityCalculationTyperequired

fixed or custom.

metadata.liquidity.liquidityPerDaynumber

USD value per day (when calculationType is fixed).

metadata.liquidity.customFunctionstring

Custom calculation formula (when calculationType is custom).

Developers Contribution

Star / Fork / Become a Collaborator (github_repo_star, github_repo_fork, github_repo_collaborator)

metadata.githubRepoUrlstringrequired

Repository URL. Must match https://github.com/<owner>/<repo>.

Merge a GitHub PR (github_merge_PR)

metadata.githubRepoUrlstringrequired

Repository URL. Must match https://github.com/<owner>/<repo>.

metadata.githubBranchNamestring

Target branch to count merged PRs against. Defaults to main if omitted.

metadata.rangeArray<RewardAmountRange>

Optional tiered rewards by number of merged PRs.

Other

Submit Text Input (text_input)

metadata.verifyPlaceHolderTextstring

Placeholder text for the input field.

metadata.verificationTextMinimumLengthnumber

Minimum character length required.

Enter a Code (code_entry)

Reward users for entering promo codes.

metadata.promoCodeTypePromoCodeTyperequired

Type: code, csv, or generate.

metadata.promoCodestring

Single shared code (when promoCodeType is code).

metadata.promoCodeCsvUrlstring

CSV URL with unique codes (when promoCodeType is csv).

metadata.promoCodeLengthnumber

Number of codes to generate (when promoCodeType is generate).

metadata.isRestrictedToNewUsersboolean

Only allow new users (last 6 hours) to redeem.

metadata.linkstringrequired

URL the user clicks.

metadata.timeDelayToVerifySecondsnumber

Delay (1–600 s) before the reward is granted.

metadata.socialPlatformSocialPlatform

Platform shown for the link (e.g. X, Discord, or Custom).

metadata.buttonTextstring

Label on the action button.

Answer a Quiz (quiz)

question.textstringrequired

Question text.

question.choicesArray<Choice>

Answer options with isCorrect flag. Exactly one choice must be correct.

Answer a Poll (poll)

question.textstringrequired

Poll question text.

question.choicesArray<Choice>

Answer choices.

question.allowMultipleAttemptsboolean

Allow users to vote multiple times.

Participate in Governance Vote (snapshot_governance)

metadata.snapshotProposalsArray<{ id: string, space: string }>required

Snapshot proposal(s) to track. One proposal per rule.

Create a Partner Account (create_partner_account)

metadata.linkstringrequired

Partner site URL. Must match the partner auth domain.

metadata.buttonTextstring

Label on the action button.

External Rule (external_rule)

Custom rules managed by external services.

amountnumberrequired

Base points amount.

trackProgressboolean

Track completion progress (top-level field).

metadata.rangeArray<RewardAmountRange>

Range-based rewards with custom amounts.

metadata.rewardPerActionboolean

Multiply reward by number of actions.

metadata.conditionDescriptionstring

Description of what the external rule tracks.

Points Airdrop (points_airdrop)

Airdrop points to specific users via CSV.

loyaltyUserAllotmentCsvUrlstringrequired

CSV URL with wallet addresses and point amounts (top-level field).

trackProgressboolean

Track processing progress.

dApp Activity

Rank and reward dApps by on-chain activity. These rules rely on several top-level fields in addition to metadata.range, and route rewards to the dApp owner (isDappRule).

Applies to: dapps_by_gas_spent, dapps_by_active_users, dapps_by_new_users, dapps_by_transaction_count.

metadata.rangeArray<RewardAmountRange>required

Tiered rewards by rank/threshold for the chosen metric.

dappDataWindowLoyaltyRuleIntervalrequired

Data window for the metric: daily, weekly, or monthly. Leave empty for all-time.

dappDeployedWithinLoyaltyRuleInterval

Optional filter limiting to dApps deployed within this window.

isDappRuleboolean

Routes rewards to the dApp owner. Immutable after creation.

networkNetworkTyperequired

Blockchain network for the rule (required when isDappRule is true).

dapps_by_user_retention appears in the dashboard but is not yet implemented — creating it will fail. Use one of the four supported dApp metrics above.

Range-Based Rewards

Some rules support range-based rewards with tiered amounts:

metadata.rangeArray<RewardAmountRange>

Array of reward ranges.

startRangenumberrequired

Minimum value for this range.

endRangenumberrequired

Maximum value (use Number.MAX_SAFE_INTEGER for unlimited).

amountnumberrequired

Points awarded for this range.

loyaltyBadgeIdstring

Optional badge for this range.

Range Examples

"metadata": {
  "range": [
    {
      "startRange": 1,
      "endRange": 10,
      "amount": 100
    },
    {
      "startRange": 11,
      "endRange": 50,
      "amount": 500
    },
    {
      "startRange": 51,
      "endRange": 9223372036854775807,
      "amount": 1000
    }
  ]
}
"metadata": {
  "range": [
    {
      "startRange": 1,
      "endRange": 9223372036854775807,
      "amount": 1.5
    }
  ],
  "isTokenHoldMultiplier": true
}

Advanced Configuration

Max Reward Limits

maxAmountPerIntervalnumber

Maximum points users can earn in the interval.

maxAmountIntervalLoyaltyRuleMaxAmountInterval

Interval period: daily, weekly, monthly.

Claim Type

claimTypeLoyaltyRuleClaimType

manual (user must claim) or auto (auto-rewarded).

Reward Lifetime

rewardLifetimeLoyaltyRuleRewardLifetime

For multipliers/badges: permanent (never removed) or dynamic (removed if requirements not met).

Airdrop Duration

effectiveStartTimeISO8601

When airdrop becomes effective.

effectiveEndTimeISO8601

When airdrop ends.

Validation Examples

{
  "organizationId": "org_123",
  "websiteId": "website_456",
  "name": "Daily Login",
  "type": "check_in",
  "frequency": "daily",
  "interval": "daily",
  "startTime": "2024-01-01T00:00:00Z",
  "rewardType": "points",
  "amount": 10,
  "loyaltyCurrencyId": "currency_789",
  "network": "ethereum",
  "metadata": {}
}
{
  "organizationId": "org_123",
  "websiteId": "website_456",
  "name": "Hold Premium NFT",
  "type": "TokenHold",
  "network": "mainnet",
  "collections": [{
    "address": "0xBC4CA0EdA7647A8aB7C2061c2E118A18a936f13D",
    "network": "mainnet"
  }],
  "frequency": "daily",
  "interval": "daily",
  "startTime": "2024-01-01T00:00:00Z",
  "endTime": "2024-12-31T23:59:59Z",
  "rewardType": "points",
  "amount": 100,
  "loyaltyCurrencyId": "currency_789",
  "metadata": {
    "hasCustomRewardsUrl": true,
    "customRewardsApiUrl": "https://api.example.com/rewards",
    "customRewardsApiKey": "key_xyz"
  }
}
{
  "organizationId": "org_123",
  "websiteId": "website_456",
  "name": "Tweet Challenge",
  "type": "drip_x_new_tweet",
  "network": "ethereum",
  "frequency": "daily",
  "interval": "unlimited",
  "startTime": "2024-01-01T00:00:00Z",
  "rewardType": "points",
  "loyaltyCurrencyId": "currency_789",
  "metadata": {
    "checkText": ["#project", "launch"],
    "requirePostLink": true,
    "requirePostMediaLink": false,
    "range": [
      {
        "startRange": 1,
        "endRange": 5,
        "amount": 10
      },
      {
        "startRange": 6,
        "endRange": 20,
        "amount": 50
      },
      {
        "startRange": 21,
        "endRange": 9223372036854775807,
        "amount": 100
      }
    ]
  }
}

Error Handling

All required fields must be provided. Missing required fields will result in a 400 Bad Request error with details about which fields are missing.

Range values must not overlap. The start range should be less than or equal to the end range for each range entry.

For Ultra networks (Ultra/UltraTestnet), the collection address format is address::symbol. For example: accountname::TOKEN.

Use the loyaltyCurrencyId field consistently across rules to ensure proper currency management in your loyalty program.

Rule Type Limitations and Restrictions

Multiplier Reward Type Support

The multiplier reward type is NOT supported for the following rule types:

  • check_in
  • link_click
  • referred_user
  • points_airdrop

Only points and badge reward types are available for these rules.

Claim Type Support

Different rule types support different claim types (manual vs auto-reward):

These rules require users to manually claim rewards:

  • check_in
  • text_input
  • poll
  • quiz
  • create_partner_account
  • telegram_join
  • drip_x_follow
  • drip_x_tweet
  • drip_x_new_tweet
  • swap

Reward Lifetime Options

The rewardLifetime field (for multipliers and badges) has different support levels:

Most rules only support permanent lifetime where rewards never expire:

  • check_in
  • twitter_followers
  • Retweet
  • BoughtOn, SoldOn, MintOn
  • twitter_like, twitter_comment, twitter_post_hashtag
  • profile_completed, referred_user
  • poll, quiz
  • Most social and connection rules

With permanent lifetime, once a user qualifies for the reward, they keep it forever even if they stop meeting requirements.

These rules support both permanent and dynamic lifetime:

  • TokenHold - Most flexible, supports both
  • external_rule - Can expire if user stops meeting requirements

With dynamic lifetime, rewards are removed if the user no longer meets the requirements (e.g., sold their NFT).

Interval Restrictions

Some rules have limited interval options:

Rules with frequency: immediately typically only support interval: once or specific intervals.

Rules with frequency: none may support custom intervals.

Range-Based Rewards

Range-based rewards are ONLY supported for:

  • TokenHold
  • BoughtOn, SoldOn, MintOn
  • token_hold_erc20
  • twitter_followers (with rewardCriteria)
  • external_rule (with trackProgress)
  • shopify_spend

Range-based rewards require the metadata.range array. Maximum range value is Number.MAX_SAFE_INTEGER (use this for unlimited).

Network-Specific Limitations

For Ultra and UltraTestnet networks, collection addresses require a symbol suffix: address::symbol (e.g., accountname::TOKEN)

Token Holdings

ERC20 token holding rules (token_hold_erc20) require network support for USD price tracking. Networks without USD support will disable the rewardPerValue option.

Liquidity Pool Limits

Liquidity rules support a maximum of 20 pools per rule, all within the same network and protocol with the same factory address.

Shopify Integration

Shopify spend rules require providing both shopifyStoreUrl and shopifyStoreAccessToken. The access token is used for one-time authentication during rule creation but is not stored.