Skip to main content

Overview

Our asset creation tab in the admin tool consists of three separate flows that comprise the entire contract set and minting & auction page creation process:
  1. Contract set up
  2. Metadata configuration / asset creation
  3. Listing or auction creation

Step by step

Contract set up

  1. Access the Asset Creation tab in the menu on the left and hit the create new contract button in the top right.
Snag admin asset creation tab with create new contract button
  1. Hit the ‘Create New Contract’ button on the next modal to confirm you’ll be the owner on the contract. On the next tab you’ll:
    1. Input the collection name, symbol, and image along with optional description and external link.
    2. Choose the token type (ERC-721 or ERC-1155). Only one token type can exist on the NFT smart contract.
    3. Choose chain
    4. Optional: enable meta transaction if you plan to enable gasless transactions in the future via Stratus relayers .
    5. Choose the wallet address for mint and royalty payouts
Contract setup modal with collection name, symbol, and image fields
Contract details with token type, chain, and payout wallet settings

Token gating

Token gating lets you restrict who can claim or buy from this contract by checking wallet ownership or badges at checkout. Configure it during contract set up so every listing tied to the contract uses the same gating rules.
Token gating configuration for restricting minting by wallet holdings
When token gating is enabled, you can choose which assets grant access (NFTs, tokens, specific collections, or badges) and set the minimum balance required. You can use this to:
  • limit claims to holders of a partner collection
  • require a minimum token balance before minting
  • unlock drops for badge holders or onchain members
Configuration options:
  • Free-claim versions are supported by the checkbox under each option. Users can claim for free if they own the required tokens or badges.
  • Limit the amount of mints to the number of tokens or badges the user owns.
  • Require the user to own all the required tokens or badges. In this case, they will be able to mint only one item.

Add Assets to Contract

  1. The next step is to configure metadata to the contract as individual assets. We support individual asset creation to start, with .CSV upload and other multi-asset upload capabilities coming soon.
    1. Add asset name, description, info about the artist, media, and optional trait info
    2. Optional fields
      1. Add quantity limit: maximum amount mintable/wallet
      2. Add minimum: minimum amount to mint/transaction
      3. Burn to redeem: if you want users to burn the NFT to redeem it. You can add a field they need to fill in, show a blank text or collect shipping details(needs API integration).
    3. Click ‘Finish’ when you’re done or ‘Duplicate’ / ‘New Asset’ to add more
Asset metadata form with name, description, artist info, and media upload
Asset optional fields for quantity limit, minimum mint, and burn to redeem

Listing

  1. You can now hit ‘List Assets’ on the contract modal in the assets creation tab when ready to either list, create an auction or create a sweepstakes.
List assets button on the contract modal in asset creation tab
Listing type selection with purchase, auction, and sweepstakes options

List assets for purchase

Listing configuration form for currency, price, and duration settings
  1. Choose the currency you’d like to list in (native currency or loyalty points if enabled)
  2. Choose the price and the asset you’d like to list
    1. You can select “free claim”, if you want users to claim for free and pay only gas fees.
  3. Choose listing duration start and (optional) end time
Allowlist (optional)
  • You can add a allowlist to the listing to restrict who can claim the asset.
  • You can either upload a CSV file with the wallet addresses or add them via the API
  • Allowlisted wallets can always claim the asset for free, only paying gas fees.

List assets for auction

  1. Add an auction name, description, duration, and currency
  2. Note: Auctions can be tied to one or more than one onchain asset, so please add sufficient description to explain to the user what they’re bidding on
  3. Choose your auction mechanics:
    1. Minimum Bid: The minimum amount any bidder can start the auction with
    2. Minimum Bid Increment: The minimum increase on any bid from the previous highest bid
    3. Reserve Price: The minimum bid that is valid to win the auction, this is the most important of these three inputs
  4. Multi-Winner Auctions Only: By default, multi-winner auctions charge each bidder the amount they bid, if ‘Reverse Dutch Auction’ is toggled we’ll instead refund all winners the difference of their bid and the lowest price winning bid
  5. Blind Auctions Only: If toggled on, users can only see their own bids. All other bids are hidden
  6. Choose the prize!
Auction setup form with name, description, duration, and bid settings
Auction prize selection and multi-winner auction options

List assets for sweepstakes

  1. Add the sweepstakes name, description, duration, and entry token purchase currency
  2. Choose the entry ticket and specify the total number of available tickets.
  3. Choose the prize(s).
Sweepstakes setup form with name, description, and entry ticket config
Sweepstakes prize selection and ticket availability settings
  1. You’re live! View these listings as part of your marketplace!

Hide a collection from template pages

To prevent a collection from appearing on template pages (for example, the Loyalty page), you can hide it from the Rewards Shop.
  1. Click the ‘Edit Rewards page’ button in the top right of the Rewards Shop.
Edit Rewards page button in Snag admin Rewards Shop
  1. In the Edit Rewards page settings, use the visibility control to hide or unhide the collection so it does not show on template pages (e.g., Loyalty).
Visibility toggle to hide or show a collection on template pages

API-based integration

Use the minting API to power a custom checkout or backend-driven flow. This mirrors the same flow used by the Rewards Shop frontend.
API requests use the X-API-KEY header. Use the assetId and contractId (UUID or onchain address) from the Rewards Shop.
1

Create a mint request

Call POST /api/minting/contracts/mint with the assetId, contractId, walletAddress, and quantity
{
  "assetId": "asset_uuid",
  "contractId": "contract_uuid_or_address",
  "walletAddress": "0xabc...",
  "quantity": 1,
}
The response returns mintingContractAssetMintStatusId, which you will poll for status and signature.
2

Poll for a signature (or relayer tx)

Call GET /api/minting/status/{id} every 1-2 seconds until a signature is returned or the status moves to failed.
  • If signature is returned, you need to submit the onchain transaction yourself.
  • If relayerTxId is present, this is a gasless transaction and you can ignore the next step.
3

Execute the onchain mint

Use the returned signature to execute the mint on the correct chain by calling the mint function on the contract.
  • EVM: call mintWithSignature for ERC-721/1155. If currency is an ERC-20 and price > 0, approve price * quantity before minting.
  • Solana: execute the base64 transaction using the provided blockhash and last valid block height.
This is how our internal implementation looks like. For contract interaction we use:
  import {
    sendTransaction,
    getContract,
    waitForReceipt
  } from "thirdweb";
const buyToken = async (
  props?: BuyTokenProps
): Promise<string | undefined> => {
  const res = await axios.post(`/api/minting/contracts/mint`, {
    quantity,
    assetId,
    shippingId: props?.shippingId,
    shippingOrderType: props?.shippingOrderType,
    emailAddress: props?.emailAddress,
    contractId: contractAddress,
    agreedToSendW9: props?.agreedToSendW9,
    customInputValue: props?.customInputValue,
  })

  if (res.status !== 202) {
    return undefined
  }

  const mintStatusId = res.data.mintingContractAssetMintStatusId

  while (true) {
    const { data } = await axios.get(`/api/minting/status/${mintStatusId}`)

    if (data.relayerTxId && data.status === MintStatus.minted) {
      return data.txHash
    }

    if (data.signature) {
      const signature = JSON.parse(data.signature)

      if (network === NetworkType.solana) {
        const umi = createUmi()

        const { signature: txSig } = await executeBase64Txn(
          umi,
          signature.signature,
          signature.latest
        )
        await updateMintStatus(mintStatusId, {
          status: MintStatus.minted,
          txHash: txSig,
        })
        return txSig
      }

      const contract = getContract({
        address: contractAddress,
        client: thirdwebClient,
        chain: defineThirdwebChain(chainId),
      })
      const fnToUse =
        tokenType === 'erc721'
          ? mintWithSignatureErc721
          : mintWithSignatureErc1155
      const preparedTx = fnToUse({
        contract,
        signature: signature.signature,
        payload: signature.payload,
      })
      const sentTx = await sendTransaction({
        account,
        transaction: preparedTx,
      })
      await waitForReceipt({
        client: thirdwebClient,
        chain: defineThirdwebChain(chainId),
        transactionHash: sentTx.transactionHash,
      })
      await axios.post(`/api/minting/status/${mintStatusId}`, {
        status: 'minted',
        txHash: sentTx.transactionHash,
      })
      return sentTx.transactionHash
    }
  }
4

Update mint status (optional)

After the transaction confirms, you can call POST /api/minting/status/{id} with status: minted and the txHash. This will be done automatically if you don’t call it, but can improve the UX.

Troubleshooting

NFTs not showing on your frontend (e.g., https://example.com/loyalty)

If some NFTs created in the admin are not visible on your public frontend, walk through the checks below.
These steps apply when your site consumes listings/collections configured in the admin. If your frontend uses a custom integration, ensure it queries the same contracts, networks, and listing types you configure here.
1

Ensure the collection is visible on template pages

Open the Rewards Shop and click ‘Edit Rewards page’ in the top right, then make sure the collection is set to Visible so it can appear on template pages (e.g., Loyalty).
After saving, the collection becomes eligible to render on template-driven pages.
2

Verify the assets are actually listed and currently active

Creating assets alone will not show them to users. Confirm you used ‘List Assets’ and that:
  • The assets are included in the listing (IDs/ranges set correctly)
  • The listing start time is in the past and the end time (if any) has not passed
  • The listing status is active
On the listing modal, toggle to ‘Unlisted Assets’ to find items you may have missed.
3

Match the page filters and currency type

Template pages may filter by collection, category, or currency. For example, a Loyalty page typically shows listings priced in loyalty points. If you listed in a different currency (e.g., native token), those items may not appear on that page.
If your page is configured to only show loyalty listings, standard crypto-priced listings won’t display there.
4

Confirm the correct chain/network

Ensure your contract and listings are on the same chain your frontend is reading from. If your site points to a different network (e.g., testnet vs mainnet), listings will not appear.
5

Allow indexing time and hard refresh the page

After creating or updating listings, allow a few minutes for indexing and caching to update, then hard refresh your frontend.
If you use server-side caching or a CDN, purge the cache for the affected pages.
  • Collection not attached to the page: In ‘Edit Rewards page’, verify the targeted collection is included in the page layout/sections. - Incorrect ID ranges: If you used ranges (e.g., 1-10), make sure all intended IDs fall within those ranges. - Missing media or required metadata: Some templates hide items with incomplete media/fields; confirm each asset has the required fields and media URLs. - Frontend filters: Check user-facing filters (collection, price type, availability) on the site aren’t excluding your items.

Solana Mint Setup & Account Initialization

On Solana, accounts must be initialized on-chain before they can be used in transactions. While initialization instructions can be added to the main transaction, this often increases the transaction size beyond Solana’s limits. Account initialization is required in two main scenarios:
  1. Asset/Token Minting: The asset or token being minted needs an initialized account.
  2. Payment Receipt: The wallet receiving payment must be initialized for the specific currency (e.g., USDC).

Solutions & UX Considerations

To handle these requirements without hitting transaction size limits, we use the following approaches:
  1. Pre-mint for Assets: We perform a “pre-mint” operation—a free mint that initializes the account for the asset/token. This keeps the main transaction size within limits. You can perform this pre-mint action directly from the admin dashboard on the listing modal. If you encounter the “Transaction Too Large” error, try running this pre-mint step first.
    Pre-mint option on the listing modal in Snag admin
    This pre-mint action currently lacks a specific success message or loading indicator in the UI. It does not count towards the visible mint count (which may still show 0), even though 1 quantity is minted on-chain.
  2. Wallet Initialization: For the payment recipient wallet, we send a small amount of the required currency (e.g., USDC or SOL) to initialize the account. This ensures the fee recipient address is ready to receive mint payments.

Common Errors

If account initialization is not handled correctly, you may encounter the following errors: Transaction Simulation Failed
Simulation failed. Message: Transaction simulation failed: Error processing Instruction 0: invalid account data for instruction.
Logs: [
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA invoke [1]",
  "Program log: Instruction: Transfer",
  "Program log: Error: InvalidAccountData",
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA consumed 2799 of 600000 compute units",
  "Program TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA failed: invalid account data for instruction"
]
Transaction Too Large
VersionedTransaction too large