Skip to main content

Subscriptions API

The Subscriptions API allows you to create and manage recurring billing subscriptions for your customers.

Methods

getSubscriptions

Retrieve a list of all subscriptions with optional pagination.

const { getSubscriptions } = useEasy();

const result = await getSubscriptions({
limit: 50,
offset: 0,
});

Parameters

  • params (optional)
    • limit (number): Maximum number of subscriptions to return (default: 10, max: 100)
    • offset (number): Number of subscriptions to skip for pagination

Returns

ApiResponse<SubscriptionData[]>;

Example

import { useEasy } from "@easylabs/react";
import { useState, useEffect } from "react";

function SubscriptionList() {
const { getSubscriptions } = useEasy();
const [subscriptions, setSubscriptions] = useState([]);
const [loading, setLoading] = useState(true);

useEffect(() => {
const fetchSubscriptions = async () => {
try {
const result = await getSubscriptions({ limit: 50 });
setSubscriptions(result.data);
} catch (error) {
console.error("Failed to fetch subscriptions:", error);
} finally {
setLoading(false);
}
};

fetchSubscriptions();
}, [getSubscriptions]);

if (loading) return <div>Loading...</div>;

return (
<ul>
{subscriptions.map((sub) => (
<li key={sub.id}>
Subscription {sub.id} - Status: {sub.status}
</li>
))}
</ul>
);
}

getSubscription

Retrieve a specific subscription by ID.

const { getSubscription } = useEasy();

const result = await getSubscription("sub_123");
const subscription = result.data;

Parameters

  • subscriptionId (string, required): The ID of the subscription to retrieve

Returns

ApiResponse<SubscriptionData>;

Example

import { useEasy } from "@easylabs/react";
import { useState, useEffect } from "react";

function SubscriptionDetails({ subscriptionId }) {
const { getSubscription } = useEasy();
const [subscription, setSubscription] = useState(null);

useEffect(() => {
const fetchSubscription = async () => {
try {
const result = await getSubscription(subscriptionId);
setSubscription(result.data);
} catch (error) {
console.error("Subscription not found:", error);
}
};

fetchSubscription();
}, [subscriptionId, getSubscription]);

if (!subscription) return <div>Loading...</div>;

return (
<div>
<h2>Subscription {subscription.id}</h2>
<p>Status: {subscription.status}</p>
<p>
Current Period:{" "}
{new Date(subscription.current_period_start).toLocaleDateString()} -{" "}
{new Date(subscription.current_period_end).toLocaleDateString()}
</p>
{subscription.cancel_at_period_end && (
<p>
<strong>Will cancel at period end</strong>
</p>
)}
</div>
);
}

createSubscription

Create a new subscription for a customer.

const { createSubscription } = useEasy();

const result = await createSubscription({
identity_id: "cust_123",
items: [{ price_id: "price_abc", quantity: 1 }],
trial_period_days: 14,
metadata: {
plan: "premium",
},
});

Parameters

  • params (required)
    • identity_id (string, required): Customer ID
    • items (array, required): Array of subscription items
      • price_id (string): Price ID
      • quantity (number): Quantity (default: 1)
    • trial_period_days (number, optional): Number of days for trial period
    • metadata (object, optional): Additional custom data

Returns

ApiResponse<SubscriptionData>;

Example

import { useEasy } from "@easylabs/react";
import { useState } from "react";

function CreateSubscriptionForm({ customerId }) {
const { createSubscription } = useEasy();
const [priceId, setPriceId] = useState("");
const [trial, setTrial] = useState(false);
const [loading, setLoading] = useState(false);

const handleSubmit = async (e) => {
e.preventDefault();
setLoading(true);

try {
const result = await createSubscription({
identity_id: customerId,
items: [{ price_id: priceId, quantity: 1 }],
trial_period_days: trial ? 14 : undefined,
metadata: {
source: "web",
},
});

console.log("Subscription created:", result.data.id);
} catch (error) {
console.error("Failed to create subscription:", error);
} finally {
setLoading(false);
}
};

return (
<form onSubmit={handleSubmit}>
<select
value={priceId}
onChange={(e) => setPriceId(e.target.value)}
required
>
<option value="">Select Plan</option>
<option value="price_monthly">Monthly - $29.99</option>
<option value="price_annual">Annual - $299.99</option>
</select>

<label>
<input
type="checkbox"
checked={trial}
onChange={(e) => setTrial(e.target.checked)}
/>
Start with 14-day trial
</label>

<button type="submit" disabled={loading}>
{loading ? "Creating..." : "Subscribe"}
</button>
</form>
);
}

updateSubscription

Update an existing subscription.

const { updateSubscription } = useEasy();

const result = await updateSubscription("sub_123", {
items: [{ price_id: "price_new", quantity: 1 }],
metadata: {
upgraded: true,
},
});

Parameters

  • subscriptionId (string, required): The ID of the subscription to update
  • params (required)
    • items (array, optional): New subscription items
    • metadata (object, optional): Updated metadata

Returns

ApiResponse<SubscriptionData>;
Subscription Changes

Updating subscription items will prorate charges and adjust billing accordingly. The change takes effect immediately.

Example

import { useEasy } from "@easylabs/react";
import { useState } from "react";

function UpgradeSubscription({ subscriptionId, currentPriceId }) {
const { updateSubscription } = useEasy();
const [newPriceId, setNewPriceId] = useState("");

const handleUpgrade = async () => {
try {
const result = await updateSubscription(subscriptionId, {
items: [{ price_id: newPriceId, quantity: 1 }],
metadata: {
upgraded_from: currentPriceId,
upgraded_at: new Date().toISOString(),
},
});

console.log("Subscription upgraded:", result.data);
} catch (error) {
console.error("Failed to upgrade:", error);
}
};

return (
<div>
<h3>Upgrade Your Plan</h3>
<select
value={newPriceId}
onChange={(e) => setNewPriceId(e.target.value)}
>
<option value="price_pro">Pro - $49.99/month</option>
<option value="price_enterprise">Enterprise - $99.99/month</option>
</select>
<button onClick={handleUpgrade}>Upgrade Now</button>
</div>
);
}

cancelSubscription

Cancel a subscription at the end of the current billing period.

const { cancelSubscription } = useEasy();

const result = await cancelSubscription("sub_123");

Parameters

  • subscriptionId (string, required): The ID of the subscription to cancel

Returns

ApiResponse<SubscriptionData>;
Cancellation Behavior

The subscription will remain active until the end of the current billing period. The customer retains access until that date.

Example

import { useEasy } from "@easylabs/react";
import { useState } from "react";

function CancelSubscriptionButton({ subscriptionId, onCancelled }) {
const { cancelSubscription } = useEasy();
const [confirming, setConfirming] = useState(false);

const handleCancel = async () => {
if (!confirming) {
setConfirming(true);
setTimeout(() => setConfirming(false), 5000); // Reset after 5 seconds
return;
}

try {
const result = await cancelSubscription(subscriptionId);
console.log("Subscription cancelled:", result.data);
onCancelled();
} catch (error) {
console.error("Failed to cancel:", error);
}
};

return (
<button onClick={handleCancel} style={{ color: "red" }}>
{confirming
? "Click again to confirm cancellation"
: "Cancel Subscription"}
</button>
);
}

reactivateSubscription

Reactivate a cancelled subscription before the period ends.

const { reactivateSubscription } = useEasy();

const result = await reactivateSubscription("sub_123");

Parameters

  • subscriptionId (string, required): The ID of the subscription to reactivate

Returns

ApiResponse<SubscriptionData>;
Reactivation

This only works if the subscription was cancelled but the current billing period hasn't ended yet. After the period ends, you'll need to create a new subscription.

Example

import { useEasy } from "@easylabs/react";

function ReactivateSubscriptionButton({ subscriptionId }) {
const { reactivateSubscription } = useEasy();

const handleReactivate = async () => {
try {
const result = await reactivateSubscription(subscriptionId);
console.log("Subscription reactivated:", result.data);
} catch (error) {
console.error("Failed to reactivate:", error);
}
};

return (
<button onClick={handleReactivate} style={{ color: "green" }}>
Reactivate Subscription
</button>
);
}

Type Definitions

SubscriptionData

interface SubscriptionData {
id: string;
identity_id: string;
status: SubscriptionStatus;
items: SubscriptionItem[];
current_period_start: string;
current_period_end: string;
cancel_at_period_end: boolean;
canceled_at?: string;
ended_at?: string;
trial_start?: string;
trial_end?: string;
metadata?: Record<string, unknown>;
created_at: string;
updated_at: string;
}

SubscriptionStatus

type SubscriptionStatus =
| "active" // Subscription is active and billing
| "canceled" // Subscription has been cancelled
| "incomplete" // Initial payment failed
| "incomplete_expired" // Initial payment failed and grace period expired
| "past_due" // Payment failed but retrying
| "paused" // Subscription is paused
| "trialing" // In trial period
| "unpaid"; // Payment failed and retries exhausted

SubscriptionItem

interface SubscriptionItem {
id: string;
price_id: string;
quantity: number;
}

CreateSubscriptionParams

interface CreateSubscriptionParams {
identity_id: string;
items: Array<{
price_id: string;
quantity?: number;
}>;
trial_period_days?: number;
metadata?: Record<string, unknown>;
}

Subscription Lifecycle

┌─────────┐
│ Created │
└────┬────┘


┌──────────┐ ┌──────────┐
│ Trialing │───▶│ Active │
└──────────┘ └────┬─────┘

┌────────────┼────────────┐
│ │ │
▼ ▼ ▼
┌─────────┐ ┌──────────┐ ┌──────────┐
│Past Due │ │ Canceled │ │ Paused │
└─────────┘ └──────────┘ └──────────┘
│ │
▼ ▼
┌────────┐ ┌────────┐
│ Unpaid │ │ Ended │
└────────┘ └────────┘

Common Patterns

Subscription with Trial

const result = await createSubscription({
identity_id: "cust_123",
items: [{ price_id: "price_monthly", quantity: 1 }],
trial_period_days: 14,
});

Multiple Items (Metered Billing)

const result = await createSubscription({
identity_id: "cust_123",
items: [
{ price_id: "price_base", quantity: 1 },
{ price_id: "price_addon1", quantity: 5 },
{ price_id: "price_addon2", quantity: 3 },
],
});

Upgrade/Downgrade

// Upgrade to higher tier
await updateSubscription("sub_123", {
items: [{ price_id: "price_premium", quantity: 1 }],
});

// Downgrade to lower tier
await updateSubscription("sub_123", {
items: [{ price_id: "price_basic", quantity: 1 }],
});

Error Handling

try {
const result = await createSubscription({ ... });
} catch (error) {
if (error.message.includes('payment method')) {
console.error('No valid payment method');
} else if (error.message.includes('price')) {
console.error('Invalid price ID');
} else if (error.message.includes('customer')) {
console.error('Customer not found');
} else {
console.error('Subscription creation failed:', error);
}
}

Best Practices

  1. Always show trial periods clearly to customers
  2. Send email notifications for subscription changes
  3. Handle failed payments gracefully with retry logic
  4. Allow easy cancellation to build trust
  5. Prorate upgrades/downgrades fairly
  6. Show upcoming charges before billing
  7. Keep payment methods updated to avoid failures

Next Steps

This documentation is being migrated from the DEVELOPER_DOCS.md file.

For complete documentation, please see:

Coming soon to this documentation site!