Subscription Mutations
API operations for managing membership subscriptions in Loopwise
The subscription mutations allow you to create and manage user subscriptions to membership plans within your Loopwise school. These operations enable you to programmatically handle subscription lifecycle events.
Available mutations:
| Operation | Description |
|---|---|
createSubscription | Create a new subscription for a user |
updateSubscription | Update an existing subscription's end period |
cancelSubscription | Cancel an existing subscription for a user |
Common Use Cases
User Enrollment Automation
Use the subscription mutations to automatically enroll users in membership plans when they complete certain actions in your application or meet specific criteria.
Subscription Extension
Extend subscription periods for loyal customers or as part of a promotion campaign.
Trial Management
Manage trial periods by creating time-limited subscriptions and extending or shortening them based on user engagement.
Lifetime Access
Create lifetime subscriptions for users who purchase permanent access plans. Lifetime subscriptions:
- Grant permanent access with no expiration (
endAtisnull) - Have no billing periods (
currentPeriodEndisnull) - Cannot be cancelled or updated (
isCancellableisfalse) - Are one-time purchases with no recurring charges
Plan Types
Different plan types have different behaviors when creating subscriptions:
| Plan Type | Description | endAt | currentPeriodEnd | Cancellable |
|---|---|---|---|---|
recurring | Regular subscription with recurring billing | Calculated | Billing period end | Yes |
fixed_date | Expires on a specific date | Set date | Set date | Yes |
specific_length | Expires after a specific duration | Calculated | Calculated | Yes |
lifetime | Permanent access, one-time purchase | null | null | No |
Create a Subscription
The createSubscription mutation allows you to programmatically create a new subscription for a user to a specific membership plan. This enables automatic enrollment in premium content or membership benefits.
Input Parameters
| Field | Type | Description |
|---|---|---|
email | String! | Email of the user to create subscription for |
name | String | Name of the user (required if user doesn't exist) |
planId | String! | ID of the membership plan |
expireAt | Int | Expiration date for fixed_date or specific_length plans (Unix Timestamp) |
initialChargeAt | Int | Initial charge date for recurring plans (Unix Timestamp) |
Note: For lifetime plans, neither
expireAtnorinitialChargeAtparameters are applicable as lifetime subscriptions grant permanent access with no expiration or recurring billing.
Return Type
type AdminCreateSubscriptionPayload {
# Array of error messages, if any occurred during the operation
errors: [String!]!
# The created subscription object, null if operation failed
subscription: AdminSubscription
}AdminSubscription Object
type AdminSubscription {
# Optional reason for subscription cancellation
cancelReason: String
# Type of cancellation, if applicable
cancelType: String
# When the subscription was created
createdAt: Int!
# End of the current billing period
currentPeriodEnd: Int
# Start of the current billing period
currentPeriodStart: Int
# Unix timestamp when the subscription ends
endAt: Int
# Unique identifier for the subscription
id: String!
# Indicates whether this subscription will be cancelled in the next billing cycle
isCanceling: Boolean!
# Indicates whether this subscription can be cancelled
isCancellable: Boolean!
# Timestamp of the next scheduled charge (null for non-recurring or inactive subscriptions)
nextChargeDate: Int
# The membership plan associated with this subscription
plan: MembershipPlan!
# The ID of the membership plan
planId: String!
# Unix timestamp when the subscription starts
startAt: Int
# The current state of the subscription
state: String!
# When the subscription was last updated
updatedAt: Int!
# The user who owns this subscription
user: User!
}Example
mutation CreateSubscription($email: String!, $planId: String!, $name: String) {
createSubscription(
email: $email
name: $name
planId: $planId
) {
errors
subscription {
id
state
planId
startAt
endAt
currentPeriodStart
currentPeriodEnd
nextChargeDate
user {
id
email
name
}
plan {
id
name
interval
intervalCount
}
}
}
}Variables
{
"email": "john@example.com",
"name": "John Doe",
"planId": "plan_456"
}Sample Response
{
"data": {
"createSubscription": {
"errors": [],
"subscription": {
"id": "sub_12345",
"state": "active",
"planId": "plan_456",
"startAt": 1687436400,
"endAt": null,
"currentPeriodStart": 1687436400,
"currentPeriodEnd": 1690028799,
"nextChargeDate": 1690028799,
"user": {
"id": "user_789",
"email": "john@example.com",
"name": "John Doe"
},
"plan": {
"id": "plan_456",
"name": "Premium Monthly",
"interval": "month",
"intervalCount": 1
}
}
}
}
}Common Errors
| Error | Description |
|---|---|
Plan not found | The specified plan ID does not exist |
User already subscribed to this plan | The user already has an active subscription to this plan |
Name is required for new users | When creating a subscription for a new user, name must be provided |
Invalid plan type | The expireAt parameter is only valid for fixed_date plan types |
Unauthorized | The API key does not have permission to create subscriptions |
Usage Scenarios
Creating a Subscription for an Existing User
mutation CreateExistingUserSubscription {
createSubscription(
email: "existing@example.com",
planId: "plan_monthly_123"
) {
errors
subscription {
id
state
user { email }
plan { name }
}
}
}Creating a Subscription with Specific Expiration
For fixed_date or specific_length plan types, you can specify when the subscription should expire:
mutation CreateFixedSubscription {
createSubscription(
email: "student@example.com",
name: "New Student",
planId: "plan_course_456",
expireAt: 1704067199 # December 31, 2023 23:59:59 UTC
) {
errors
subscription {
id
endAt
}
}
}Creating a Lifetime Subscription
For lifetime plans, the subscription grants permanent access with no expiration. No additional parameters like expireAt or initialChargeAt are needed:
mutation CreateLifetimeSubscription {
createSubscription(
email: "member@example.com",
name: "Premium Member",
planId: "plan_lifetime_123"
) {
errors
subscription {
id
state
endAt # Will be null (permanent access)
currentPeriodEnd # Will be null (no billing period)
nextChargeDate # Will be null (one-time payment)
plan {
id
name
planType # Will be "lifetime"
isLifetime # Will be true
}
}
}
}Key characteristics of lifetime subscriptions:
endAtisnull— the subscription never expirescurrentPeriodEndisnull— there is no billing periodnextChargeDateisnull— there are no recurring chargesisCancellableisfalse— lifetime subscriptions cannot be cancelled
Update a Subscription
The updateSubscription mutation allows you to update an existing subscription's end period. This mutation is particularly useful for extending or shortening subscription periods based on business rules or customer service needs.
Input Parameters
| Field | Type | Description |
|---|---|---|
id | String! | The ID of the subscription to update |
currentPeriodEnd | Int! | New end date for the current subscription period (Unix Timestamp) |
Note: Lifetime subscriptions cannot have their period updated. Attempting to update a lifetime subscription will return an error, as lifetime plans grant permanent access and do not have billing periods.
Return Type
type AdminUpdateSubscriptionPayload {
# Array of error messages, if any occurred during the operation
errors: [String!]!
# The updated subscription object, null if operation failed
subscription: Subscription
}Subscription Object
type Subscription {
# The unique identifier for the subscription
id: String!
# The timestamp when the current billing period ends
currentPeriodEnd: Int
# The timestamp when the current billing period started
currentPeriodStart: Int
# The timestamp when the subscription will end (for non-recurring subscriptions)
endAt: Int
# Indicates whether this subscription will be cancelled at the end of the current period
isCanceling: Boolean!
# Indicates whether this subscription can be cancelled
isCancellable: Boolean!
# The timestamp of the next scheduled charge (null for non-recurring subscriptions)
nextChargeDate: Int
# List of payments associated with this subscription
payments: [Payment!]
# The membership plan associated with this subscription
plan: MembershipPlan!
# The ID of the membership plan
planId: String!
# The timestamp when the subscription started
startAt: Int
# The current state of the subscription
# Possible values: active, incomplete, incomplete_expired, trialing, past_due, canceled, expired
state: String!
# The user who owns this subscription
user: User!
}Example
mutation UpdateSubscription {
updateSubscription(
id: "sub_12345",
currentPeriodEnd: 1704067199
) {
errors
subscription {
id
currentPeriodEnd
currentPeriodStart
state
isCanceling
user {
id
name
email
}
plan {
id
name
}
}
}
}Sample Response
{
"data": {
"updateSubscription": {
"errors": [],
"subscription": {
"id": "sub_12345",
"currentPeriodEnd": "1704067199",
"currentPeriodStart": "1701388800",
"state": "active",
"isCanceling": false,
"user": {
"id": "user_789",
"name": "John Doe",
"email": "john@example.com"
},
"plan": {
"id": "plan_456",
"name": "Premium Annual"
}
}
}
}
}Restrictions for Canceling Subscriptions
If a subscription is in a canceling state (i.e., isCanceling: true — the user has already requested cancellation but it's scheduled for a future date), all update operations are blocked. This is consistent with the UI behavior.
| Operation | Allowed? | Error Message |
|---|---|---|
| Extend period | No | "Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead." |
| Shorten period | No | "Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead." |
| Set past timestamp | No | "Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead." |
Note: To immediately cancel a subscription that is pending cancellation, use the
cancelSubscriptionmutation instead. ThecancelSubscriptionmutation will immediately terminate the subscription regardless of the originally scheduled cancellation date.
Common Errors
| Error | Description |
|---|---|
Subscription not found | The specified subscription ID does not exist |
Cannot update period for lifetime subscriptions | Lifetime plans grant permanent access and do not have billing periods that can be updated |
Cannot update an already cancelled subscription | The subscription has already been fully cancelled (state: canceled) |
Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead. | The subscription is in canceling state; all operations are blocked. Use CancelSubscription for early cancellation. |
Timestamp cannot be more than 10 years in the future | The provided timestamp exceeds the maximum allowed value |
Timestamp cannot be more than 1 year in the past | The provided timestamp is too far in the past |
Cannot set end date earlier than current period start | The new end date cannot be before the subscription's current period start date |
Usage Scenarios
Extending a Subscription
Extend a subscription that's about to expire for a loyal customer:
mutation ExtendSubscription {
updateSubscription(
id: "sub_12345",
currentPeriodEnd: 1706745599 # Example: January 31, 2024 23:59:59 UTC
) {
subscription {
id
currentPeriodEnd
user { name }
}
errors
}
}Shortening a Trial Period
Adjust the end date of a trial subscription:
mutation ShortenTrial {
updateSubscription(
id: "sub_trial_789",
currentPeriodEnd: 1689465599 # Example: July 15, 2023 23:59:59 UTC
) {
subscription {
id
currentPeriodEnd
state
}
errors
}
}Cancel a Subscription
The cancelSubscription mutation allows you to programmatically cancel an active subscription for a user. This enables you to revoke access to premium content or membership benefits at the end of the current billing period or immediately.
Input Parameters
| Field | Type | Required | Description |
|---|---|---|---|
id | String! | Yes | ID of the subscription to cancel |
cancelAtPeriodEnd | Boolean | No | If true, cancels at period end; if false, cancels immediately or at specified time (default: true) |
customEndedAt | Int | No | Specific time to end the subscription as Unix Timestamp (seconds since epoch). Overrides the default behavior |
Note: Lifetime subscriptions cannot be cancelled as they represent one-time purchases with permanent access. The
isCancellablefield will returnfalsefor lifetime subscriptions.
Return Type
type AdminCancelSubscriptionPayload {
# Array of error messages, if any occurred during the operation
errors: [String!]!
# The cancelled subscription object, null if operation failed
subscription: Subscription
}Subscription Object
type Subscription {
id: String!
# Unix Timestamp. The date when the subscription was cancelled. Null if not cancelled.
canceledAt: Int
# Unix Timestamp. The end date of the current billing period. For active subscriptions, this represents when the next payment will be due.
currentPeriodEnd: Int
# Unix Timestamp. The start date of the current billing period.
currentPeriodStart: Int
# Unix Timestamp. The date when the subscription ends. For cancelled subscriptions, this represents the final termination date.
endAt: Int
# Indicates whether this subscription will be cancelled in the next billing cycle
isCanceling: Boolean!
# Indicates whether this subscription can be cancelled
isCancellable: Boolean!
# Timestamp of the next scheduled charge. Null for non-recurring or inactive subscriptions.
nextChargeDate: Int
payments: [Payment!]
plan: MembershipPlan!
planId: String!
# Unix Timestamp. The date when the subscription starts.
startAt: Int
# Possible values: active, incomplete, incomplete_expired, trialing, past_due, canceled, expired
state: String!
# The user who owns this subscription
user: User!
}Example
mutation CancelSubscription {
cancelSubscription(
id: "sub_12345"
cancelAtPeriodEnd: true
) {
errors
subscription {
id
planId
startAt
endAt
isCanceling
state
user {
id
name
email
}
}
}
}Example — Immediate Cancellation
mutation CancelSubscriptionImmediately {
cancelSubscription(
id: "sub_12345"
cancelAtPeriodEnd: false
) {
errors
subscription {
id
state
endAt
canceledAt
}
}
}Early Cancellation (for subscriptions pending cancellation)
If a subscription is already in a canceling state (i.e., isCanceling: true — the user has already requested cancellation but it's scheduled for a future date), you can use this mutation to immediately cancel the subscription.
Behavior
When calling cancelSubscription on a subscription that is already canceling:
| Aspect | Behavior |
|---|---|
cancelAtPeriodEnd parameter | Ignored — always cancels immediately |
customEndedAt parameter | Ignored — always cancels immediately |
| Result | Subscription is immediately cancelled (state: "canceled") |
cancelAt | Set to null (scheduled cancellation is cleared) |
endAt | Set to current time |
Example — Early Cancel a Pending Cancellation
mutation EarlyCancelSubscription {
cancelSubscription(
id: "sub_12345"
) {
errors
subscription {
id
state
isCanceling
cancelAt
endAt
canceledAt
}
}
}Sample Response — Early Cancellation
{
"data": {
"cancelSubscription": {
"errors": [],
"subscription": {
"id": "1af2d4fc-1bfb-4e03-a11d-6254bf175adb",
"state": "canceled",
"isCanceling": false,
"cancelAt": null,
"endAt": 1736956800,
"canceledAt": 1736956800
}
}
}
}Note: This is useful when a student has already initiated a cancellation request, but an administrator needs to immediately terminate the subscription (e.g., due to a refund agreement or policy violation).
Sample Response
{
"data": {
"cancelSubscription": {
"errors": [],
"subscription": {
"id": "1af2d4fc-1bfb-4e03-a11d-6254bf175adb",
"planId": "sub_id_12345",
"startAt": 1745561281,
"endAt": 1748153281,
"isCanceling": true,
"state": "active",
"user": {
"id": "abaed4c4-4d48-4118-a3e8-34d89d540335",
"name": "Student",
"email": "student@example.com"
}
}
}
}
}Common Errors
| Error | Description |
|---|---|
Subscription not found | The specified subscription ID does not exist |
Subscription already cancelled | The subscription is already in a cancelled state |
Subscription is not cancellable | The subscription cannot be cancelled. This applies to lifetime subscriptions which represent one-time purchases with permanent access |