Recurring payments
Recurring crypto billing without card-on-file
Every subscription product you have used works by keeping an instrument on file and charging it. This one has no instrument to keep. Each subscriber gets a permanent deposit address derived from your wallet, and the arrival of their payment is what renews the period — so there is nothing stored that could be charged without them, and nothing to leak that would let anyone else charge them either.
How a renewal actually happens
The whole cycle is push, not pull. Read this section and you know exactly what the system does, because there is not much more to it.
- 01
One permanent address per subscriber
When you create a subscription, the subscriber is given a deposit address derived from your own wallet, and it does not change between periods. They can save it, automate sending to it from their own wallet, or pay it manually each period. You never ask them for credentials, and you never store any.
- 02
A period clock, not a charge attempt
A subscription carries the start and end of its current period. Nothing is attempted at the end of it — no retry schedule, no dunning, no failed charge. The period simply lapses unless a payment arrives, and a payment arriving is what moves it forward.
- 03
Payment arrives, renewal is evaluated
When money lands on that address, it is evaluated against the state the subscription is in. If the period had lapsed or is due, it counts as a renewal, and you receive the exact amount status — paid, underpaid or overpaid — so your app decides what access that buys. A payment that arrives late is evaluated as a renewal on arrival, not discarded.
- 04
A new subscription starts with no access
Subscriptions begin pending, not active. Access starts when the first payment is detected, which means there is no window where someone holds an active subscription they have not paid for. It also means your app should treat pending as “not entitled yet”.
What happens to money that arrives on an already-paid cycle
It is recorded as credit against that subscriber. You choose what to do with it: apply it toward the next period, or leave it as extra. Either way you are told it happened rather than having to reconcile a surprise later.
Credit is bookkeeping, not custody. The funds are already sitting on your own address; the credit balance is an accounting number describing what arrived, not a balance we hold on your behalf. There is nothing to withdraw, because there is nothing being kept.
The webhooks your app receives
Subscription state is only useful if your application hears about it. These are the events published today, in the order they occur across a period.
- subscription.renewed
- A payment was evaluated against a due or lapsed subscription. Carries the amount status — paid, underpaid or overpaid — plus the amounts and your own reference for the subscriber, so your app can decide what to unlock.
- subscription.expiring_soon
- The current period lapses within the reminder lead time you configure. This is the hook for your own reminder email, sent before anything is lost.
- subscription.expired
- The period lapsed without payment. Nobody was charged and nothing was attempted; the subscription simply stopped being current, and your app should revoke access.
- subscription.credit_added
- A payment landed on a subscription whose current period is already fully paid, and was recorded as credit rather than treated as a renewal.
Every subscription webhook is signed, delivered with retries, and carries your own external reference for the subscriber, so the receiving side never has to guess who it is about.
How recurring works without card-on-file
These follow directly from having no stored instrument to charge. Design around them and the model works.
- You cannot charge a subscriber. There is no stored instrument and no pull, so every period requires the subscriber to send — which is exactly why nothing can be charged without them.
- A subscriber who forgets simply lapses. The expiring-soon webhook exists so you can remind them, but the reminder is yours to send and the decision stays theirs.
- A plan priced in crypto moves with the market between periods. Price in a stablecoin if you want the amount to mean the same thing next month.
- There is no chargeback and no reversal. Refunds are a payment you send back, on your own terms, from your own wallet.
- Granting and revoking access is your application’s job. Unheld tells you what happened and when; it does not sit in front of your product.
Questions
Can Unheld take the payment automatically each month?
No, and it is not an omission. There is no card or mandate on file, so nothing can be pulled — the subscriber sends to their permanent deposit address and that payment is what renews the period. Subscribers who want it hands-off can automate the transfer from their own wallet, which keeps the initiative with them rather than with us.
What happens if a subscriber pays late?
The payment is evaluated as a renewal when it arrives, not rejected for being late. You get the renewal webhook at that point and decide whether the new period runs from the payment or from the original schedule. Money is never stranded for arriving after a deadline.
What if they pay the wrong amount?
You are told exactly which it was. A renewal carries an amount status of paid, underpaid or overpaid along with the figures, so your application can grant full access, hold it, or record the extra as credit. Nothing is silently rounded or absorbed.
How does a subscriber cancel?
By not paying. There is no mandate to revoke, no card to remove and no support ticket needed to stop the money — the next period simply lapses and the subscription expires. You can also end one from your side at any time.
Try a full cycle on testnet
Create a plan, subscribe a test customer, pay the address and watch the renewal webhook arrive — all on a free testnet, before any real money is involved.
Start free