Developers
AutoPay Agent documentation
Non-custodial recurring payments on Solana. Merchants create plans and payment links. Subscribers sign one transaction that pays the first period and approves a capped SPL allowance. A billing agent then pulls each renewal on schedule, straight from the subscriber's token account to the merchant's wallet.
How it works
- Checkout. The server builds an unsigned v0 transaction: create the merchant's token account if missing,
TransferCheckedfor the first period (skipped during a trial),ApproveChecked(delegate = billing agent, amount = cap), and a memo. The subscriber's wallet signs and sends it, and the server verifies it on-chain before the subscription is created. - Shared approval. An SPL token account holds exactly one delegate and one amount, and
approvereplaces it rather than adding to it. So the cap a subscriber signs includes the remaining allowance of their other live AutoPay subscriptions in the same token. Each subscription keeps its own share in the database. - Renewals. Every 30 seconds the worker picks up due subscriptions. It checks the token account on-chain (owner, mint, delegate, delegated amount, balance, frozen), then the agent signs one
TransferCheckedas delegate for exactly one period's price, with a fresh reference key and a memoautopay:v1|renew|…. Preflight simulation rejects anything that would fail, at no cost. - No double charges. A charge is recorded as pending with its signature before confirmation. If the worker restarts, it resolves the pending signature before ever sending another transfer.
- Billing dates advance from the scheduled date, not the retry date. Monthly plans keep their anchor day (Jan 31 → Feb 28 → Mar 31).
Trust model
Non-custodial. Funds never sit with AutoPay Agent; every payment is one wallet-to-wallet transaction. The billing agent (EzTWj52AYoEphjYJrKZumLyqfhNiqBuNDXiNjDsgomQR) is an ordinary keypair held by our worker. It pays network fees for renewals.
What a subscriber risks. At most the remaining approved amount of one token. The agent cannot touch SOL, NFTs or other tokens, and a revoke takes effect on-chain immediately. The honest caveat: SPL delegation does not restrict the destination, so the cap is enforced by the token program but the merchant destination and the once-per-period rule are enforced by our worker. Pick caps you are comfortable with. Removing this assumption is the first roadmap item: a subscription program with a PDA delegate.
Cancel any time. Canceling on /account stops charges immediately, and the same click offers a transaction that reduces or revokes the on-chain approval. Any wallet's revoke tool works too.
Billing rules & failure codes
Each plan sets retryAttempts (0–10), retryHours between attempts, onFinalFailure (cancel | pause | past_due) and remindHours (reminder before each charge, with an early balance/approval check). Both sides get every failure with its reason: in-app, by email if configured, and as a charge.failed webhook. Platform-side failures (RPC outage, agent out of SOL) are deferred and never count as an attempt.
| insufficient_balance | The wallet did not hold enough tokens for this charge. |
| allowance_insufficient | The remaining approved allowance is lower than the charge. Re-approve to continue. |
| delegate_changed | The approval was revoked or replaced by another app. Re-approve to continue. |
| token_account_missing | The token account no longer exists. |
| account_frozen | The token account is frozen by the token issuer. |
| agent_unfunded | The billing agent is out of SOL for network fees (not your fault — it will retry automatically). |
| network | The Solana network or RPC did not respond (it will retry automatically). |
| tx_failed | The charge transaction failed on-chain. |
| unknown | The charge could not be completed. |
REST API
Base URL https://autopayagent.online. Authenticate with a secret key from Dashboard → Developers: Authorization: Bearer apk_live_…. Keys are stored hashed and shown once. Amounts are decimal strings in token units ("29.5"); responses include both amountRaw (base units) and amount. Rate limit: 600 requests/minute per key.
| POST | /api/v1/checkouts | Create a one-time checkout {token, amount, title?, redirectUrl?, email?, metadata?} → id, url, solanaPayUrl, reference |
| GET | /api/v1/checkouts/:id | Checkout status (pending | paid | expired), payer, signature, metadata |
| GET | /api/v1/plans | List plans with live subscriber counts |
| POST | /api/v1/plans | Create a plan {name, token, amount, intervalUnit, intervalCount, trialDays, allowanceCycles, retryAttempts, retryHours, onFinalFailure, remindHours} |
| PATCH | /api/v1/plans/:id | Update copy, billing rules or status (price/token/interval are immutable) |
| GET | /api/v1/payment-links | List payment links (one-time + one subscribe link per plan) |
| POST | /api/v1/payment-links | Create a one-time link {title, token, amount, description?, redirectUrl?} |
| GET | /api/v1/subscriptions | List subscriptions (?status=active|trialing|past_due|paused|completed|canceled, ?plan_id=) |
| GET | /api/v1/subscriptions/:id | One subscription with its payment attempts |
| POST | /api/v1/subscriptions/:id/cancel | Cancel now; no further charges |
| GET | /api/v1/payments | Payment history (?status=, ?limit=, ?format=csv) |
| GET | /api/v1/overview | MRR, live subscribers, 30-day volume, failed charges, upcoming charges |
| GET | /api/v1/notifications | Workspace notices |
| GET | /api/v1/webhooks | Recent webhook deliveries; POST sends a signed test.ping |
curl -X POST https://autopayagent.online/api/v1/plans \
-H "Authorization: Bearer $AUTOPAY_KEY" -H "content-type: application/json" \
-d '{"name":"Pro","token":"USDC","amount":"29","intervalUnit":"month",
"trialDays":7,"retryAttempts":3,"retryHours":24,"onFinalFailure":"pause"}'
# share the plan's subscribe link (GET /api/v1/payment-links → kind "subscription")
# or send customers to https://autopayagent.online/subscribe/<plan slug>Webhooks
Set an https URL under Dashboard → Developers. Deliveries are JSON { id, type, created, data } with headers AutoPay-Event, AutoPay-Event-Id and AutoPay-Signature: t=<unix>,v1=<hex>, where v1 = HMAC-SHA256(secret, `${t}.${rawBody}`). Non-2xx responses are retried after 1m, 5m, 30m, 2h, 6h, 12h and 24h. Handle events idempotently by id.
| checkout.paid | A one-time checkout was paid and verified on-chain |
| subscription.created | Checkout signed: first period paid (or trial started) and allowance approved |
| subscription.renewed | A renewal charge landed |
| subscription.reminder | A charge is coming up (plan.remindHours before); includes at_risk |
| charge.failed | A charge failed; failure.code, attempt, next_retry_at or final |
| subscription.past_due / .paused / .canceled | Final billing-rule outcome or a manual cancel |
| subscription.completed | All approved cycles were paid; the subscriber must approve more |
| subscription.extended / .reactivated | The subscriber approved more cycles (and reactivated) |
import crypto from 'node:crypto';
export function verify(rawBody, header, secret) {
const [, t, v1] = /t=(\d+),v1=([0-9a-f]{64})/.exec(header || '') || [];
if (!t || Math.abs(Date.now() / 1000 - Number(t)) > 300) return false;
const want = crypto.createHmac('sha256', secret).update(t + '.' + rawBody).digest('hex');
return crypto.timingSafeEqual(Buffer.from(want), Buffer.from(v1));
}Solana Pay & reference keys
Every checkout gets a unique reference public key, added as a read-only account to the payment's transfer instruction. The QR code is a Solana Pay transaction request (solana:https://autopayagent.online/api/solana-pay/<checkout id>): the wallet fetches the exact transaction, including the optional platform fee split, from the server. The worker finds payments with getSignaturesForAddress(reference) and checks recipient, mint, amount and fee before marking the checkout paid. So a phone payment is confirmed even if the browser tab is closed. Checkouts expire after 30 minutes.
Roadmap
Not built yet.
On-chain subscription program · Next
Replace the shared agent delegate with a per-subscription PDA that can only pay the approved merchant, at most once per period, enforced by the program itself.
Pay in any token · Planned
Subscribers pay in SOL or any SPL token; a Jupiter route swaps to the merchant's settlement stablecoin inside the same charge.
Usage-based billing · Planned
Metered plans: report usage over the API, the agent charges the computed amount within the approved cap.
Token-2022 stablecoins · Planned
PYUSD, USDG and other Token-2022 assets, including transfer-fee aware accounting.
Invoices & tax exports · Exploring
PDF invoices and receipts, VAT/GST fields, and accounting exports beyond CSV.
Teams & SDKs · Exploring
Multiple members per merchant workspace with roles, plus TypeScript/Python SDKs and an embeddable checkout.