XRP Ledger Standards

XLS-0078
Draft
   xls: 78
   title: Subscriptions
   description: A subscription mechanism for recurring payments on the XRP Ledger
   author: Kris Dangerfield (@krisdangerfield), Denis Angell (@angell_denis)
   discussion-from: https://github.com/XRPLF/XRPL-Standards/discussions/222
   status: Draft
   category: Amendment
   created: 2024-08-30
   updated: 2025-09-10

XLS-78: Subscriptions

Abstract

This proposal introduces a subscription (recurring payments) mechanism to the XRP Ledger (XRPL), enabling functionality similar to direct debits. The amendment allows account owners to authorize automated recurring payments with predefined parameters such as amount, frequency, and destination, supporting XRP, Trustline-based tokens (IOUs), and Multi-Purpose Tokens (MPTs).

Motivation

Currently, payments on the XRP Ledger require the sender to initiate and sign each transaction. While off-chain solutions like Xaman API have introduced pull payments where transactions can be generated by the destination and signed by the sender, this still requires per-transaction authorization, which is impractical for subscription services.

This proposal addresses this limitation by introducing a subscription feature that allows pre-authorized recurring payments, improving user experience and enabling a broader range of services on the XRP Ledger. The subscription model balances user convenience with security by allowing pre-authorized payments within defined parameters while maintaining user control over amounts and frequency.

1. Implementation

This amendment introduces new ledger objects and transactions to support recurring payments for XRP, IOUs, and MPTs, accounting for the specific behaviors and constraints associated with each token type.

2. Specification

2.1. Ledger Entries

2.1.1. Subscription

2.1.1.1. Object Identifier

The Subscription object ID is computed as the SHA-512Half of:

  • The Subscription space key (0x0055)
  • The Account ID
  • The Destination ID
  • The Transaction Sequence
2.1.1.2. Fields
Field Name Constant Required Internal Type Default Value Description
LedgerEntryType Yes Yes UINT16 0x0055 Identifies this as a Subscription object
Flags No Yes UINT32 0 Reserved for future use
PreviousTxnID No Yes HASH256 N/A Hash of the transaction that most recently modified this object
PreviousTxnLgrSeq No Yes UINT32 N/A Ledger index containing the transaction that most recently modified this object
Account Yes Yes ACCOUNTID N/A The account that owns the subscription
Destination Yes Yes ACCOUNTID N/A The account authorized to receive subscription payments
Data Yes Conditional BLOB N/A Data field for payment categorization
SendMax No Yes AMOUNT N/A Maximum amount that can be withdrawn per period (XRP, IOU, or MPT)
Balance No Yes AMOUNT N/A Remaining balance available for the current period
Frequency Yes Yes UINT32 N/A Time period in seconds between consecutive payments (minimum 3600)
NextClaimTime No Yes UINT32 N/A Ripple epoch time when the next payment can be claimed
StartTime Yes Conditional UINT32 N/A Ripple epoch time when the subscription started (set at creation)
Expiration Yes Conditional UINT32 N/A Ripple epoch time when the subscription expires
Sequence Yes Yes UINT32 N/A Transaction sequence number used to create this subscription
OwnerNode No Yes UINT64 N/A Page of the source account's owner directory
DestinationNode No Yes UINT64 N/A Page of the destination account's owner directory
2.1.1.3. Ownership

The Subscription object is linked to two OwnerDirectory entries:

  • The source account's OwnerDirectory (via OwnerNode) - increments owner count
  • The destination account's OwnerDirectory (via DestinationNode) - does not increment owner count

Only the source account's owner count is affected because they bear the reserve requirement.

2.1.1.4. Reserves

Creating a Subscription object increases the owner's XRP reserve by one increment. This reserve is returned when the subscription is deleted.

2.1.1.5. Deletion

The Subscription can be deleted through:

  • SubscriptionCancel transaction from either the owner or destination
  • SubscriptionClaim transaction when the expiration time is reached and the claim is successful
  • Account deletion is blocked while any subscriptions exist (either as owner or destination)
2.1.1.6. Freeze/Lock Compliance

For IOUs:

  • Global Freeze: Prevents creation and claims
  • Individual Freeze: Prevents creation and claims for frozen account
  • Deep Freeze: Prevents all operations

For MPTs:

  • Lock Conditions: Prevent creation and claims for locked accounts
2.1.1.7. Invariants

Before and after any transaction:

  • BalanceSendMax
  • NextClaimTimeStartTime (if StartTime present)
  • Expiration > NextClaimTime (if Expiration present)
  • Frequency > 0
  • AccountDestination
  • If non-XRP: asset must exist and be valid
2.1.1.8. Example JSON
{
  "LedgerEntryType": "Subscription",
  "Flags": 0,
  "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
  "Destination": "rLdCa1mLK5R5Am25ArfXFmqgNwjZgnfy91",
  "Data": "DEADBEEF",
  "SendMax": "100000000",
  "Balance": "50000000",
  "Frequency": 2592000,
  "NextClaimTime": 711232800,
  "StartTime": 708640800,
  "Expiration": 721600800,
  "Sequence": 42,
  "OwnerNode": "0000000000000001",
  "DestinationNode": "0000000000000002",
  "PreviousTxnID": "E8A6F9B99B041DF5C932DF8DA29B2A84B42D4D4F0F4A08931D76D62F42E1F1B9",
  "PreviousTxnLgrSeq": 8675309
}

2.2. Transactions

2.2.1. SubscriptionSet

Creates a new subscription or updates an existing one.

2.2.1.1. Fields
Field Name Required? JSON Type Internal Type Default Value Description
TransactionType Yes String UINT16 N/A Value: "SubscriptionSet"
Destination Conditional String ACCOUNTID N/A Destination account (required for creation, forbidden for updates)
Data No String BLOB N/A Data field for categorization
Amount Yes Object/String AMOUNT N/A Maximum amount per period (XRP, IOU, or MPT)
Frequency Conditional Number UINT32 N/A Period in seconds between payments (required for creation, forbidden for updates)
StartTime No Number UINT32 Current Time When subscription starts (creation only)
Expiration No Number UINT32 N/A When subscription expires
SubscriptionID Conditional String HASH256 N/A ID for updates (mutually exclusive with creation fields)
2.2.1.2. Failure Conditions

Creation Mode (no SubscriptionID):

  1. General Validation:
  2. Destination equals Account (temDST_IS_SRC)
  3. Destination doesn't exist (tecNO_DST)
  4. Destination requires tag but none provided (tecDST_TAG_NEEDED)
  5. Amount ≤ 0 or invalid (temBAD_AMOUNT)
  6. Frequency ≤ 0 (temMALFORMED)
  7. StartTime < current time (temMALFORMED)
  8. Expiration < current time or StartTime (temBAD_EXPIRATION)
  9. Insufficient reserve (tecINSUFFICIENT_RESERVE)

  10. XRP-specific:

  11. Amount not positive or exceeds max XRP (temBAD_AMOUNT)

  12. IOU-specific:

  13. Bad currency (temBAD_CURRENCY)
  14. Issuer doesn't exist (tecNO_ISSUER)
  15. Account lacks trustline (tecNO_LINE)
  16. Account not authorized when lsfRequireAuth set (tecNO_AUTH)
  17. Destination not authorized when lsfRequireAuth set (tecNO_AUTH)
  18. Account frozen (tecFROZEN)
  19. Destination frozen (tecFROZEN)
  20. Insufficient spendable balance (tecINSUFFICIENT_FUNDS)
  21. Precision loss in amount (tecPRECISION_LOSS)

  22. MPT-specific:

  23. MPT issuance doesn't exist (tecOBJECT_NOT_FOUND)
  24. Account doesn't hold MPT (tecOBJECT_NOT_FOUND)
  25. MPT lacks tfMPTCanTransfer flag unless destination is issuer (tecNO_AUTH)
  26. Account not authorized when tfMPTRequireAuth set (tecNO_AUTH)
  27. Destination not authorized when tfMPTRequireAuth set (tecNO_AUTH)
  28. Account locked (tecLOCKED)
  29. Destination locked (tecLOCKED)
  30. Insufficient spendable balance (tecINSUFFICIENT_FUNDS)

Update Mode (with SubscriptionID):

  1. Update Validation:
  2. XRP, IOU and MPT Validation (as above)
  3. SubscriptionID doesn't exist (tecNO_ENTRY)
  4. Account not owner (tecNO_PERMISSION)
  5. Invalid Amount (temBAD_AMOUNT)
  6. Expiration in past or before NextClaimTime (temBAD_EXPIRATION)
  7. Destination, Frequency, or StartTime present (temMALFORMED)
  8. Asset type differs from original (tecWRONG_ASSET)
2.2.1.3. State Changes

Creation:

  1. Create Subscription object with all specified fields
  2. Set Balance = Amount
  3. Set NextClaimTime = StartTime (if provided) or current ledger time
  4. Add to source account's owner directory (increment owner count)
  5. Add to destination account's owner directory (no owner count change)
  6. Deduct reserve from source account

Update:

  1. Update Amount field
  2. If Balance > new Amount, set Balance = Amount
  3. Update Expiration if provided
2.2.1.4. Example JSON

Creation:

{
  "TransactionType": "SubscriptionSet",
  "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
  "Destination": "rLdCa1mLK5R5Am25ArfXFmqgNwjZgnfy91",
  "Data": "DEADBEEF",
  "Amount": {
    "currency": "USD",
    "value": "100",
    "issuer": "rUSDIssuerAddress"
  },
  "Frequency": 2592000,
  "StartTime": 711232800,
  "Expiration": 721600800,
  "Fee": "12",
  "Sequence": 42
}

Update:

{
  "TransactionType": "SubscriptionSet",
  "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
  "SubscriptionID": "E8A6F9B99B041DF5C932DF8DA29B2A84B42D4D4F0F4A08931D76D62F42E1F1B9",
  "Amount": "150000000",
  "Expiration": 724192800,
  "Fee": "12",
  "Sequence": 43
}

2.2.2. SubscriptionCancel

Cancels an existing subscription.

2.2.2.1. Fields
Field Name Required? JSON Type Internal Type Default Value Description
TransactionType Yes String UINT16 N/A Value: "SubscriptionCancel"
SubscriptionID Yes String HASH256 N/A ID of subscription to cancel
2.2.2.2. Failure Conditions
  1. SubscriptionID doesn't exist (tecNO_ENTRY)
  2. Account not owner or destination (tecNO_PERMISSION)
2.2.2.3. State Changes
  1. Remove from source account's owner directory
  2. Remove from destination account's owner directory
  3. Decrement source account's owner count
  4. Return reserve to source account
  5. Delete Subscription object
2.2.2.4. Example JSON
{
  "TransactionType": "SubscriptionCancel",
  "Account": "r9cZA1mLK5R5Am25ArfXFmqgNwjZgnfk59",
  "SubscriptionID": "E8A6F9B99B041DF5C932DF8DA29B2A84B42D4D4F0F4A08931D76D62F42E1F1B9",
  "Fee": "12",
  "Sequence": 44
}

2.2.3. SubscriptionClaim

Claims a payment from an active subscription.

2.2.3.1. Fields
Field Name Required? JSON Type Internal Type Default Value Description
TransactionType Yes String UINT16 N/A Value: "SubscriptionClaim"
SubscriptionID Yes String HASH256 N/A ID of subscription to claim
Amount Yes Object/String AMOUNT N/A Amount to claim (≤ available balance)
2.2.3.2. Failure Conditions
  1. General Validation:
  2. SubscriptionID doesn't exist (tecNO_ENTRY)
  3. Account not destination (tecNO_PERMISSION)
  4. Account equals owner (tecNO_PERMISSION)
  5. Wrong asset type (tecWRONG_ASSET)
  6. Amount > subscription SendMax (temBAD_AMOUNT)
  7. Amount > available Balance (tecINSUFFICIENT_FUNDS)
  8. Current time < NextClaimTime unless in arrears (tecTOO_SOON)

  9. XRP-specific:

  10. Source has insufficient liquid XRP (tecINSUFFICIENT_FUNDS)

  11. IOU-specific:

  12. Source lacks trustline (tecNO_LINE)
  13. Source has insufficient balance (tecINSUFFICIENT_FUNDS)
  14. Source not authorized (tecNO_AUTH)
  15. Destination not authorized (tecNO_AUTH)
  16. Source frozen (tecFROZEN)
  17. Destination frozen (deep freeze only) (tecFROZEN)
  18. Cannot create destination trustline - insufficient reserve (tecNO_LINE_INSUF_RESERVE)
  19. Cannot create destination trustline - authorization required (tecNO_AUTH)

  20. MPT-specific:

  21. Source doesn't hold MPT (tecOBJECT_NOT_FOUND)
  22. Source has insufficient balance (tecINSUFFICIENT_FUNDS)
  23. MPT cannot be transferred unless destination is issuer (tecNO_AUTH)
  24. Source not authorized (tecNO_AUTH)
  25. Destination not authorized (tecNO_AUTH)
  26. Source locked (tecLOCKED)
  27. Destination locked (tecLOCKED)
  28. Cannot create destination MPToken - insufficient reserve (tecINSUFFICIENT_RESERVE)
  29. Cannot create destination MPToken - authorization required (tecNO_AUTH)
2.2.3.3. State Changes
  1. Arrears Handling:
  2. If currentTime ≥ NextClaimTime + Frequency AND Balance < SendMax:

    • Forfeit remaining Balance
    • Advance NextClaimTime by exactly one Frequency
    • Reset Balance to SendMax
  3. Token Transfer:

For XRP: - Deduct amount from source account balance - Add amount to destination account balance

For IOUs: - Auto-create trustline if needed and possible - Adjust source trustline balance (decrease) - Adjust destination trustline balance (increase) - Apply current TransferRate if applicable

For MPTs: - Auto-create MPToken if needed and possible - Adjust source MPToken balance (decrease) - Adjust destination MPToken balance (increase) - Apply current TransferFee if applicable - Handle issuer special cases (no MPToken for issuer)

  1. Balance Management:
  2. Deduct claimed amount from Balance
  3. If Balance reaches zero:

    • Advance NextClaimTime by one Frequency
    • Reset Balance to SendMax
  4. Expiration Check:

  5. If currentTime ≥ Expiration after successful claim:

    • Delete subscription (follow deletion state changes)
  6. Update Metadata:

  7. Update PreviousTxnID and PreviousTxnLgrSeq
2.2.3.4. Example JSON
{
  "TransactionType": "SubscriptionClaim",
  "Account": "rLdCa1mLK5R5Am25ArfXFmqgNwjZgnfy91",
  "SubscriptionID": "E8A6F9B99B041DF5C932DF8DA29B2A84B42D4D4F0F4A08931D76D62F42E1F1B9",
  "Amount": "50000000",
  "Fee": "12",
  "Sequence": 100
}

3. Key Differences Between Token Types in Subscriptions

Aspect XRP IOU Tokens Multi-Purpose Tokens (MPTs)
Holdings Native balance Trustline required MPToken object required
Authorization N/A lsfRequireAuth flag tfMPTRequireAuth flag
Transfer Capability Always allowed Subject to freeze Requires tfMPTCanTransfer
Freeze/Lock N/A Global/Individual/Deep freeze Lock conditions
Transfer Costs None TransferRate (current) TransferFee (current)
Auto-creation N/A Trustline during claim MPToken during claim
Reserve for Auto-creation N/A Required from destination Required from destination
Issuer Special Cases N/A Can be source or destination Doesn't hold MPToken objects

4. Arrears and Balance Management

The subscription system implements a specific arrears mechanism:

  1. Period Tracking: Each subscription tracks the current period via NextClaimTime
  2. Balance per Period: Each period has an available Balance (≤ SendMax)
  3. Partial Claims: Destinations can claim less than the full Balance
  4. Forfeit on Arrears: If a period ends with unused Balance and the next period is entered:
  5. The unused Balance is forfeited
  6. NextClaimTime advances by exactly one Frequency
  7. Balance resets to the full SendMax
  8. No Bulk Claims: Only one period can be processed per claim transaction

This design prevents transaction spam while allowing flexible billing patterns.

5. Rationale

Key design decisions and their justifications:

5.1. Single Claim per Period

Limiting to one claim per period simplifies implementation and prevents spam. Services requiring multiple payments should use appropriate frequencies (e.g., weekly instead of monthly).

5.2. Balance Tracking with Forfeit

Tracking balance within periods allows partial claims while the forfeit mechanism prevents indefinite accumulation of claimable amounts.

5.3. No Transfer Rate Storage

Using current transfer rates/fees at claim time (rather than storing at creation) simplifies the implementation and aligns with standard XRPL token transfer behavior.

5.4. Dual Ownership Model

Allowing both source and destination to cancel provides flexibility for both parties while the reserve requirement remains only with the source.

5.5. Auto-creation of Holdings

Allowing trustlines and MPTokens to be created during claims (when authorized) improves user experience for new token recipients.

6. Backwards Compatibility

This amendment introduces:

  • New ledger object type (Subscription)
  • Three new transaction types
  • No modifications to existing functionality

The amendment is fully backwards compatible. Existing transactions and ledger objects are unaffected.

7. Security Considerations

7.1. Built-in Protections

  • Amount Limits: Claims cannot exceed authorized amounts
  • Time Restrictions: Claims limited by frequency periods
  • Balance Tracking: Prevents over-claiming within periods
  • Asset Type Validation: Ensures claims match subscription asset
  • Authorization Compliance: Respects all issuer requirements
  • Reserve Requirements: Economic cost prevents spam

7.2. Potential Risks and Mitigations

Risk: Services claiming maximum amounts unnecessarily

  • Mitigation: Users can cancel at any time
  • Mitigation: Services risk losing customers through abuse

Risk: Users canceling after receiving services

  • Mitigation: Services enforce terms of service
  • Mitigation: Services can implement deposit/prepayment models

Risk: Timing attacks on period boundaries

  • Mitigation: Strict time validation in claim logic
  • Mitigation: Atomic period advancement

Risk: Griefing through subscription spam

  • Mitigation: Reserve requirements make spam expensive
  • Mitigation: Both parties can cancel unwanted subscriptions

7.3. Compliance Features

The system respects all existing XRPL compliance mechanisms:

  • IOU freeze capabilities remain effective
  • MPT lock capabilities remain effective
  • Authorization requirements are enforced
  • All standard AML/KYC controls apply

8. Reference Implementation

A reference implementation is available at: [Link to rippled PR when available]

9. FAQ

Q: Why can't I claim multiple periods at once? A: This prevents transaction spam and aligns with typical billing patterns. Services should set appropriate frequencies.

Q: What happens if I don't claim for several periods? A: You can claim as many times as needed to advance to the current period.

Q: Can partial amounts be claimed? A: Yes, useful for usage-based billing where the exact amount varies.

Q: How do transfer rates/fees work? A: Current rates are applied at claim time, not stored at creation, ensuring consistency with normal XRPL transfers.

Q: Can subscriptions be paused? A: Not directly, but services can claim zero amounts or users can cancel and recreate subscriptions.

Q: What if the destination doesn't have a trustline/MPToken? A: If authorization isn't required and the destination has sufficient reserve, these are auto-created during the first claim.

Q: Can I update the frequency or destination? A: No, these are immutable. Cancel and create a new subscription if changes are needed.