A Subscription is an on-chain record tying an agent wallet to a Plan. It gives the service provider permission to pull a set amount of USDC from the agent’s wallet every billing cycle, and the agent never has to sign each individual transaction.

Subscription structure

interface Subscription {
  id: string;                     // On-chain subscription id
  subscriber: Address;            // Agent wallet
  plan: string;                   // Id of the plan being subscribed to
  status: SubscriptionStatus;     // TRIAL | ACTIVE | PAUSED | CANCELLED
  startedAt: number;              // Unix timestamp
  trialEndsAt?: number;           // Unix timestamp, if applicable
  nextBillingAt: number;          // Unix timestamp of next collection
  cycleCount: number;             // Number of completed billing cycles
  authorizedAmount: number;       // Max pull per cycle (in token base units)
}

Lifecycle

subscribe()
     |
     v
  TRIAL  (if trialPeriodDays > 0)
     |
     |  trial period ends
     v
  ACTIVE  <---+
     |        |
     |        |  billing cycle completes, collection succeeds
     |        |
     |  collection fails (insufficient funds)
     v
  PAUSED  -->--+  (after retry window, if still failing)
     |
     |  agent cancels or provider cancels
     v
 CANCELLED

TRIAL

When a plan carries a trial period, the subscription begins in TRIAL status. Nothing gets collected while the trial runs. Billing starts with the first cycle after the trial ends.

ACTIVE

An ACTIVE subscription bills itself at every renewal date. Either the provider’s system or HoodLayer’s automation calls hoodlayer.collect() to pull funds from the subscriber’s wallet, and the on-chain Subscription Authority checks the authorization before executing the transfer.

PAUSED

When a collection attempt fails (say the agent wallet doesn’t hold enough USDC), the subscription moves to PAUSED. HoodLayer keeps retrying over a configurable window (default: 3 attempts over 7 days). If every retry fails, the subscription stays paused and a subscription.payment_failed webhook fires.

Once funds are topped up, either the agent or the provider can unpause manually.

CANCELLED

Cancelling stops billing right away, and the cancellation gets written on-chain. Whatever time was already paid for in the current cycle stays accessible (enforcing any grace period is the provider’s job).

How automatic billing works

Smart contracts on Robinhood Chain can’t pull from a wallet unless that wallet has explicitly authorized it. HoodLayer handles this with its own open-source HoodLayerSubscriptions contract, deployed on Robinhood Chain.

When an agent runs agent.subscribe(), the SDK writes a subscription record on-chain and delegates a Subscription Authority to the provider for that exact (wallet, token, authorized_amount) combination. The authority is narrowly scoped: the provider can pull at most the authorized amount per billing cycle, nothing more.

Come renewal time, the provider (or HoodLayer’s automation) calls collect() on the contract. It checks the subscription record, confirms the authorization still holds, and runs the token transfer. No individual billing transaction ever needs the agent’s signature.

Creating a subscription

From the agent side:

const sub = await agent.subscribe({
  planId: "0x7f3a...plan_id",
});

console.log(sub.id);            // on-chain subscription id
console.log(sub.status);        // "TRIAL" or "ACTIVE"
console.log(sub.nextBillingAt); // Unix timestamp

Verifying a subscription

On the provider side, checking whether an incoming request comes with an active subscription looks like this:

const isValid = await hoodlayer.verifySubscription({
  subscriber: requestingWallet,
  plan: plan.id,
});

if (!isValid) {
  return res.status(403).json({ error: "No active subscription" });
}

In most setups the payment gate middleware does this check for you.

Cancellation

// The agent ends their own subscription
await agent.cancelSubscription({ subscriptionId: sub.id });

// The provider ends it (e.g. for policy violations)
await hoodlayer.cancelSubscription({ subscriptionId: sub.id });

A cancellation is an on-chain operation, final and publicly verifiable.

Viewing active subscriptions

// Agent side: every active subscription for this wallet
const subscriptions = await agent.listSubscriptions();

// Provider side: everyone subscribed to a plan
const subscribers = await hoodlayer.listSubscribers({ plan: plan.id });

Subscriptions also show up in the HoodLayer Dashboard, next to MRR trends, churn rate, and per-subscriber transaction history.