# NFT Collection

> Non-fungible token collection with unique token IDs, metadata URIs, and badge-based ownership

**Category:** Token Types

## Summary

Required standards: \["NFTs"]

* For tradable NFTs: \["NFTs", "NFTMarketplace", "NFTPricingDenom:ubadge"]
* validTokenIds: set to the range of unique token IDs (e.g. \[{ "start": "1", "end": "100" }])
* Each token ID represents a unique NFT; amount in transfers is typically "1"
* Use {id} placeholder in tokenMetadata URI for per-token metadata (e.g. "ipfs\://QmHash/{id}")
* Mint approvals MUST have overridesFromOutgoingApprovals: true
* Ownership times are usually forever for NFTs

## Instructions

## NFT Collection Configuration

When creating an NFT collection, follow this pattern:

### Required Configuration

1. **Standards**: Include "NFTs" in the standards array
   * Example: "standards": \["NFTs"]
   * For tradable NFTs: "standards": \["NFTs", "NFTMarketplace", "NFTPricingDenom:ubadge"]
2. **validTokenIds**: Set to the range of unique token IDs
   * Example for 100 NFTs: \[{ "start": "1", "end": "100" }]
   * Each token ID represents a unique NFT
3. **Token Metadata**: Each tokenMetadata entry must include tokenIds matching the range
   * Use {id} placeholder for per-token metadata URIs

### Pattern Example

```json
{
  "updateValidTokenIds": true,
  "validTokenIds": [{ "start": "1", "end": "100" }],
  "updateCollectionMetadata": true,
  "collectionMetadata": {
    "uri": "ipfs://QmCollectionMetadata",
    "customData": ""
  },
  "updateTokenMetadata": true,
  "tokenMetadata": [{
    "uri": "ipfs://QmTokenMetadata/{id}",
    "customData": "",
    "tokenIds": [{ "start": "1", "end": "100" }]
  }],
  "updateCollectionApprovals": true,
  "collectionApprovals": [{
    "fromListId": "Mint",
    "toListId": "All",
    "initiatedByListId": "bb1creator...",
    "approvalId": "manager-mint",
    "tokenIds": [{ "start": "1", "end": "100" }],
    "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "approvalCriteria": {
      "overridesFromOutgoingApprovals": true
    }
  }],
  "updateStandards": true,
  "standards": ["NFTs"]
}
```

### Sequential Minting with predeterminedBalances

For NFTs, use predeterminedBalances with incrementTokenIdsBy: "1" to mint tokens sequentially (token 1, then 2, then 3, etc.):

```json
{
  "approvalCriteria": {
    "overridesFromOutgoingApprovals": true,
    "predeterminedBalances": {
      "manualBalances": [],
      "incrementedBalances": {
        "startBalances": [{ "amount": "1", "tokenIds": [{ "start": "1", "end": "1" }], "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }] }],
        "incrementTokenIdsBy": "1",
        "incrementOwnershipTimesBy": "0",
        "durationFromTimestamp": "0",
        "allowOverrideTimestamp": false,
        "recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" },
        "allowOverrideWithAnyValidToken": false
      },
      "orderCalculationMethod": {
        "useOverallNumTransfers": true,
        "usePerToAddressNumTransfers": false,
        "usePerFromAddressNumTransfers": false,
        "usePerInitiatedByAddressNumTransfers": false,
        "useMerkleChallengeLeafIndex": false,
        "challengeTrackerId": ""
      }
    },
    "maxNumTransfers": {
      "overallMaxNumTransfers": "100",
      "perInitiatedByAddressMaxNumTransfers": "1",
      "perToAddressMaxNumTransfers": "0",
      "perFromAddressMaxNumTransfers": "0",
      "amountTrackerId": "nft-mint-tracker",
      "resetTimeIntervals": { "startTime": "0", "intervalLength": "0" }
    }
  }
}
```

Key: incrementTokenIdsBy: "1" means each mint gets the next sequential token ID. Use maxNumTransfers to cap total mints and per-user mints.

IMPORTANT: predeterminedBalances and approvalAmounts are INCOMPATIBLE — use one or the other. NFTs use predeterminedBalances (not approvalAmounts).

### NFT-Specific Gotchas

* Each token ID is unique and represents a distinct NFT
* Amount in transfers is typically "1" (one NFT per transfer)
* Ownership times are usually forever for NFTs
* Mint approvals MUST have overridesFromOutgoingApprovals: true
* Use {id} in metadata URIs for per-token metadata

## Common Mistakes

* DON'T reuse token IDs across editions without understanding ownership times — each token ID is unique and represents a distinct NFT.
* DON'T forget tokenIds in canUpdateTokenMetadata permission structure — the permission must specify which token ID ranges it covers.
* DON'T use {id} in metadata name, description, or image fields — the {id} placeholder only works in the URI string itself (e.g. "ipfs\://abc/{id}").
* DON'T forget overridesFromOutgoingApprovals: true on Mint approvals — required for all minting operations.
* DON'T use custom list IDs — only reserved IDs are valid: "All", "Mint", "!Mint", "AllWithoutMint", or direct bb1... addresses.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.bitbadges.io/token-standard/skills/nft-collection.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
