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