Recur Docs

Subscriptions

Query and manage subscriptions via the SDK.

Subscriptions

Query subscription data for your app — active subscribers, payment history, and on-chain state. Methods that hit the merchant API use the apiKey you passed to new RecurClient(...). Subscriber methods take a JWT per call.

All API methods return an ApiResponse<T>:

interface ApiResponse<T> {
  success: boolean;
  data: T | null;
  error: { code: string; message: string; details?: unknown } | null;
  pagination?: { page: number; limit: number; total: number; totalPages: number };
}

Always check res.success before reading res.data.

Merchant: list subscriptions

const res = await recur.listSubscriptions(appId, { page: 1, limit: 50 });
 
if (res.success && res.data) {
  for (const sub of res.data) {
    console.log(sub.subscriptionPda, sub.status);
  }
}

listSubscriptions(appId, options?) only paginates — filter client-side on status if needed.

Merchant: get payment history

const res = await recur.getPaymentHistory(appId, { page: 1, limit: 100 });
 
if (res.success && res.data) {
  for (const tx of res.data) {
    console.log(
      `${tx.txSignature}: $${Number(tx.amountNet) / 1e6} net (${tx.status})`,
    );
  }
}

Subscriber: my subscriptions

Authenticated as a subscriber, list all subscriptions for the wallet that owns the JWT.

const res = await recur.getMySubscriptions(authToken, { page: 1, limit: 20 });

Subscriber: a single subscription by PDA

Looks up a subscription by its on-chain PDA across the subscriber's records.

const res = await recur.getSubscription(subscriptionPda, authToken);
if (res.success && res.data) {
  console.log(res.data.status, res.data.nextPaymentDue);
}

Register an on-chain subscription

After buildSubscribeTransaction confirms on-chain, register it with the Recur API using the subscriber JWT. The Keeper's chain scanner is a fallback if this call is missed.

const res = await recur.registerSubscription(
  { appId, planId, subscriptionPda: subscriptionPda.toBase58() },
  authToken,
);

Read on-chain state

Fetch the raw on-chain Subscription account directly from Solana — no API call:

const account = await recur.getSubscriptionAccount(subscriptionPda);
 
if (account) {
  console.log("subscriber:", account.subscriber.toBase58());
  console.log("merchant:", account.merchant.toBase58());
  console.log("amount:", account.amount.toString());
  console.log("interval:", account.interval.toString());
  console.log("last payment:", new Date(Number(account.lastPaymentTimestamp) * 1000));
  console.log("cancel requested:", account.cancelRequestedAt > 0n);
}

On-chain fields

FieldTypeDescription
subscriberPublicKeySubscriber wallet
merchantPublicKeyMerchant wallet
planSeednumber[]8-byte plan seed
amountbigintUSDC base units per interval
intervalbigintSeconds between payments
lastPaymentTimestampbigintUnix timestamp of last successful pull
createdAtbigintUnix timestamp of PDA creation
cancelRequestedAtbigint0 = active, non-zero = cancel timestamp
bumpnumberPDA bump seed

Derive a PDA

If you know the subscriber, merchant, and plan seed, derive the PDA without an RPC or API call:

const { pda, bump } = recur.deriveSubscriptionPda(subscriber, merchant, planSeed);

This is equivalent to using the lower-level helper:

import { findSubscriptionPda, planSeedToBuffer } from "@recur/sdk";
 
const [pda, bump] = findSubscriptionPda(
  subscriber,
  merchant,
  planSeedToBuffer(planSeed),
);

On this page