Skip to main content

Documentation Index

Fetch the complete documentation index at: https://yanhgming.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

Marlin subscriptions are built on Solana’s token delegation model. When a customer subscribes to one of your plans, they sign a single on-chain transaction that grants Marlin’s program a spending authority up to a defined cap. From that point, your customers never need to approve another transaction — Marlin charges automatically on schedule, and funds move directly from the customer’s wallet to yours.

TypeScript interfaces

The SubscriptionPlan defines the pricing and billing cadence. The Subscription tracks an individual customer’s enrollment against a plan.
export type PlanInterval = "day" | "week" | "month" | "year";

export interface SubscriptionPlan {
  id: string;
  object: "plan";
  name: string;
  description: string | null;
  currency: string;
  amount: number;
  interval: PlanInterval;
  intervalCount: number;
  trialPeriodDays: number | null;
  active: boolean;
  metadata: Record<string, string>;
  createdAt: string;
  updatedAt: string;
}

export type SubscriptionStatus =
  | "active"
  | "paused"
  | "canceled"
  | "past_due"
  | "trialing"
  | "incomplete";

export interface Subscription {
  id: string;
  object: "subscription";
  customerId: string;
  planId: string;
  status: SubscriptionStatus;
  currentPeriodStart: string;
  currentPeriodEnd: string;
  cancelAtPeriodEnd: boolean;
  canceledAt: string | null;
  pausedAt: string | null;
  trialStart: string | null;
  trialEnd: string | null;
  metadata: Record<string, string>;
  createdAt: string;
  updatedAt: string;
}

Plans

A plan is a reusable billing template you create once and attach customers to. Key fields:
  • amount — The charge per billing period in the token’s smallest unit (e.g., 9900000 for $9.90 USDC).
  • interval / intervalCount — Together these define the billing frequency. An interval of month and intervalCount of 3 means a charge every three months. The minimum period enforced on-chain is 86,400 seconds (one day).
  • trialPeriodDays — If set, new subscribers begin in trialing status for this many days before the first charge.
  • active — Inactive plans cannot accept new subscribers, but existing subscriptions on the plan continue unaffected.

Creating a plan

import Marlin from "@marlin/sdk";

const marlin = new Marlin({ apiKey: process.env.MARLIN_API_KEY! });

const plan = await marlin.plans.create({
  name: "Growth — monthly",
  currency: "USDC",
  amount: 9900000, // $9.90 USDC
  interval: "month",
  intervalCount: 1,
  trialPeriodDays: 14,
  metadata: { tier: "growth" },
});

Subscription lifecycle

The customer subscribed to a plan with a trialPeriodDays value. No charge has been collected yet. The subscription becomes active automatically when trialEnd passes and the first charge succeeds.
The customer initiated the subscribe flow but the on-chain subscribe transaction was not confirmed within the expected window. This is a transient setup failure — the customer needs to retry. No delegate authority has been granted.
The subscription is in good standing. Marlin will process the next charge when currentPeriodEnd arrives. Each successful charge advances currentPeriodStart and currentPeriodEnd by one billing interval.
The most recent charge attempt failed — either because the customer’s wallet lacked sufficient balance or the delegate authority was insufficient. Marlin fires subscription.past_due. You can resume the subscription with POST /api/subscriptions/:id/resume once the customer resolves the issue.
Either you or the customer has paused billing. No charges occur while a subscription is paused. pausedAt records when the pause began. Resume with POST /api/subscriptions/:id/resume.
The subscription has been permanently ended. canceledAt is set. Cancellation revokes the on-chain delegate authority granted during the subscribe transaction — the customer’s wallet can no longer be charged by this subscription, even if you attempt to resume it. A canceled subscription cannot be reactivated; the customer must subscribe again.

The delegated billing model

When a customer clicks “Subscribe” on your checkout page, they sign a single Solana transaction that calls the subscribe instruction. This instruction:
  1. Creates a Subscription PDA on-chain linked to the plan and the customer’s wallet.
  2. Grants Marlin’s program a token delegate authority up to max_authorized — the spending cap the customer approves.
From then on, Marlin processes charges automatically on schedule. Because the on-chain charge instruction is permissionless, charges proceed independently — but only the Marlin program controls where funds go (to your settlement wallet, minus the 50 bps fee).
If a customer needs to increase their spending cap — for example, after a plan price change — they call update_subscription_authorization to sign a new authorization transaction. You can prompt them to do this from your dashboard or via a redirect to the hosted checkout.

Pause, resume, and cancel

// Pause — stops future charges
await marlin.subscriptions.pause(subscription.id);

// Resume — restarts charges from the current period
await marlin.subscriptions.resume(subscription.id);

// Cancel — ends the subscription and revokes delegate authority
await marlin.subscriptions.cancel(subscription.id);
Cancellation is irreversible. The on-chain delegate authority is revoked as part of the cancel_subscription instruction. If you want to stop charges temporarily, use pause instead.
If cancelAtPeriodEnd is true, the subscription remains active until currentPeriodEnd, then transitions to canceled automatically.

Next steps

Recurring billing guide

Step-by-step guide to creating plans and managing the full subscription lifecycle.

Plans API reference

Full endpoint reference for creating and managing subscription plans.