# subscription

<!-- generated by site/scripts/gen-skills.ts; do not hand-edit -->

# Subscription

Instruction text for agents that use the Subscription skill, loaded by `bb dev skills subscription` and the MCP `get_skill_instructions` tool.

## Try it

Paste this into Claude Code, Codex, or Cursor with the BitBadges MCP server wired. The agent loads the skill, builds, verifies, and hands back a link to review and sign.

```text
Load the subscription skill and build me one: time-based subscription token with recurring payment approvals and auto-deletion on expiry. Use the session tools, run validate, review, and simulate in parallel, fix anything critical, then call get_review_url and give me the link.
```

From a shell, `bb dev skills subscription` prints the same instructions.

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

### Preferred path: preset (one short tool call)

The faucet approval is fully canonical. Use `subscription.faucet`:

```ts
add_preset_approval({
  presetId: "subscription.faucet",
  params: {
    paymentRecipient: "bb1...",
    paymentDenom: "<ibc/... or native>",
    paymentAmount: "<base units>",
    durationMs: "2592000000"   // monthly; daily="86400000", annual="31536000000"
  }
})
```

`list_presets({skill: "subscription"})` for params. Fall back to `add_approval` for exotic variants (multi-token tiers, manager-only mints, etc.).

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