Loopwise Docs
Admin APIMutations

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:

OperationDescription
createSubscriptionCreate a new subscription for a user
updateSubscriptionUpdate an existing subscription's end period
cancelSubscriptionCancel 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 (endAt is null)
  • Have no billing periods (currentPeriodEnd is null)
  • Cannot be cancelled or updated (isCancellable is false)
  • Are one-time purchases with no recurring charges

Plan Types

Different plan types have different behaviors when creating subscriptions:

Plan TypeDescriptionendAtcurrentPeriodEndCancellable
recurringRegular subscription with recurring billingCalculatedBilling period endYes
fixed_dateExpires on a specific dateSet dateSet dateYes
specific_lengthExpires after a specific durationCalculatedCalculatedYes
lifetimePermanent access, one-time purchasenullnullNo

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

FieldTypeDescription
emailString!Email of the user to create subscription for
nameStringName of the user (required if user doesn't exist)
planIdString!ID of the membership plan
expireAtIntExpiration date for fixed_date or specific_length plans (Unix Timestamp)
initialChargeAtIntInitial charge date for recurring plans (Unix Timestamp)

Note: For lifetime plans, neither expireAt nor initialChargeAt parameters 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

ErrorDescription
Plan not foundThe specified plan ID does not exist
User already subscribed to this planThe user already has an active subscription to this plan
Name is required for new usersWhen creating a subscription for a new user, name must be provided
Invalid plan typeThe expireAt parameter is only valid for fixed_date plan types
UnauthorizedThe 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:

  • endAt is null — the subscription never expires
  • currentPeriodEnd is null — there is no billing period
  • nextChargeDate is null — there are no recurring charges
  • isCancellable is false — 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

FieldTypeDescription
idString!The ID of the subscription to update
currentPeriodEndInt!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.

OperationAllowed?Error Message
Extend periodNo"Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead."
Shorten periodNo"Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead."
Set past timestampNo"Cannot update a subscription that is pending cancellation. Use cancelSubscription mutation instead."

Note: To immediately cancel a subscription that is pending cancellation, use the cancelSubscription mutation instead. The cancelSubscription mutation will immediately terminate the subscription regardless of the originally scheduled cancellation date.

Common Errors

ErrorDescription
Subscription not foundThe specified subscription ID does not exist
Cannot update period for lifetime subscriptionsLifetime plans grant permanent access and do not have billing periods that can be updated
Cannot update an already cancelled subscriptionThe 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 futureThe provided timestamp exceeds the maximum allowed value
Timestamp cannot be more than 1 year in the pastThe provided timestamp is too far in the past
Cannot set end date earlier than current period startThe 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

FieldTypeRequiredDescription
idString!YesID of the subscription to cancel
cancelAtPeriodEndBooleanNoIf true, cancels at period end; if false, cancels immediately or at specified time (default: true)
customEndedAtIntNoSpecific 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 isCancellable field will return false for 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:

AspectBehavior
cancelAtPeriodEnd parameterIgnored — always cancels immediately
customEndedAt parameterIgnored — always cancels immediately
ResultSubscription is immediately cancelled (state: "canceled")
cancelAtSet to null (scheduled cancellation is cleared)
endAtSet 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

ErrorDescription
Subscription not foundThe specified subscription ID does not exist
Subscription already cancelledThe subscription is already in a cancelled state
Subscription is not cancellableThe subscription cannot be cancelled. This applies to lifetime subscriptions which represent one-time purchases with permanent access

On this page