# Subscription

> Time-based subscription token with recurring payment approvals and auto-deletion on expiry

**Category:** Token Types

## Summary

Required standards: \["Subscriptions"]

* validTokenIds: MUST be exactly one token ID \[{ "start": "1", "end": "1" }]
* Subscription faucet approval requirements:
  * fromListId: "Mint"
  * overridesFromOutgoingApprovals: true
  * coinTransfers: at least 1 entry, both override flags false
  * predeterminedBalances.incrementedBalances.durationFromTimestamp: MUST be non-zero (duration in ms)
  * allowOverrideTimestamp: MUST be true
  * incrementTokenIdsBy: "0", incrementOwnershipTimesBy: "0"
  * orderCalculationMethod: MUST have exactly ONE method true (default: useOverallNumTransfers)
* Duration constants: monthly = "2592000000", annual = "31536000000", daily = "86400000"
* CRITICAL: recurringOwnershipTimes MUST be all-zeros { startTime: "0", intervalLength: "0", chargePeriodLength: "0" } — chain enforces mutual exclusivity with durationFromTimestamp

## Instructions

## Subscription Collection Configuration

When creating a subscription collection, you MUST follow these EXACT requirements:

### Required Structure

1. **Standards**: MUST include "Subscriptions"
   * "standards": \["Subscriptions"]
2. **Invariants**: MUST set noCustomOwnershipTimes to FALSE
   * Subscriptions use time-dependent ownership — this invariant MUST be false or subscriptions cannot function.
   * "invariants": { "noCustomOwnershipTimes": false, ... }
3. **validTokenIds**: MUST be exactly one token ID (per tier)
   * "validTokenIds": \[{ "start": "1", "end": "1" }]
4. **Subscription Faucet Approval Requirements**:
   * fromListId: MUST be "Mint"
   * tokenIds: MUST be exactly 1 token: \[{ "start": "1", "end": "1" }]
   * coinTransfers: MUST have at least 1 entry, NO override flags (both false)
   * predeterminedBalances.incrementedBalances:
     * durationFromTimestamp: MUST be non-zero (subscription duration in milliseconds)
     * allowOverrideTimestamp: MUST be true
     * incrementTokenIdsBy: "0"
     * incrementOwnershipTimesBy: "0"
   * orderCalculationMethod: MUST have EXACTLY ONE method set to true (default: useOverallNumTransfers)
   * overridesFromOutgoingApprovals: true (required for Mint approvals)

### Duration Constants (in milliseconds)

* Monthly: "2592000000" (30 days)
* Annual: "31536000000" (365 days)
* Daily: "86400000" (24 hours)

### Complete Example

```json
{
  "standards": ["Subscriptions"],
  "validTokenIds": [{ "start": "1", "end": "1" }],
  "collectionApprovals": [{
    "fromListId": "Mint",
    "toListId": "All",
    "initiatedByListId": "All",
    "approvalId": "subscription-mint",
    "tokenIds": [{ "start": "1", "end": "1" }],
    "transferTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }],
    "approvalCriteria": {
      "coinTransfers": [{
        "to": "bb1creator...",
        "coins": [{ "denom": "ubadge", "amount": "5000000000" }],
        "overrideFromWithApproverAddress": false,
        "overrideToWithInitiator": false
      }],
      "predeterminedBalances": {
        "incrementedBalances": {
          "startBalances": [{ "amount": "1", "tokenIds": [{ "start": "1", "end": "1" }], "ownershipTimes": [{ "start": "1", "end": "18446744073709551615" }] }],
          "incrementTokenIdsBy": "0",
          "incrementOwnershipTimesBy": "0",
          "durationFromTimestamp": "2592000000",
          "allowOverrideTimestamp": true,
          "recurringOwnershipTimes": { "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" },
          "allowOverrideWithAnyValidToken": false
        },
        "orderCalculationMethod": {
          "useOverallNumTransfers": true,
          "usePerToAddressNumTransfers": false,
          "usePerFromAddressNumTransfers": false,
          "usePerInitiatedByAddressNumTransfers": false,
          "useMerkleChallengeLeafIndex": false,
          "challengeTrackerId": ""
        },
        "manualBalances": []
      },
      "overridesFromOutgoingApprovals": true,
      "merkleChallenges": []
    }
  }]
}
```

### Subscription-Specific Gotchas

* MUST have exactly 1 token ID (not multiple)
* coinTransfers override flags MUST be false (not true)
* durationFromTimestamp MUST be non-zero
* allowOverrideTimestamp MUST be true
* **CRITICAL MUTUAL EXCLUSIVITY**: The chain enforces that ONLY ONE of `durationFromTimestamp`, `incrementOwnershipTimesBy`, or `recurringOwnershipTimes` can be non-zero. For subscriptions, use `durationFromTimestamp` and keep `recurringOwnershipTimes` as ALL ZEROS: `{ "startTime": "0", "intervalLength": "0", "chargePeriodLength": "0" }`. DO NOT set non-zero values in `recurringOwnershipTimes` — the template already has the correct structure.

## Common Mistakes

* DON'T set recurringOwnershipTimes to non-zero values — it is mutually exclusive with durationFromTimestamp. Keep all fields as "0".
* DON'T forget durationFromTimestamp must be non-zero — this is the subscription duration in milliseconds (e.g. "2592000000" for 30 days).
* DON'T forget allowOverrideTimestamp: true — subscriptions need this so each mint gets its own start timestamp.
* DON'T use multiple token IDs — subscriptions must use exactly one token ID \[{ "start": "1", "end": "1" }].
* DON'T set coinTransfers override flags to true — for standard subscription payments, both overrideFromWithApproverAddress and overrideToWithInitiator must be false.
* DON'T set noCustomOwnershipTimes: true in invariants — subscriptions require noCustomOwnershipTimes: false (or omit the invariant) because each subscription period mints a new ownershipTime window.


---

# 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/subscription.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.
